vatverify home
All guides

Handle VIES downtime with degraded cache, fallbacks, and retry patterns

VIES goes down. Here's how vatverify handles it automatically, and how your code should handle the edge cases that still matter.

TL;DR

  • VIES has real downtime: weekly maintenance, country-specific outages, peak-hour degradation
  • vatverify automatically returns degraded cache during outages with source_status: "degraded"
  • Your code should treat degraded as valid-for-now but log for later reconciliation
  • Fall back to format + checksum-only via @vatverify/vat-rates as a last resort
Package status

@vatverify/vat-rates (the offline fallback library used below) is on npm today. @vatverify/node (the SDK shown in the retry examples) ships on npm at the API launch. Until then, use fetch('https://api.vatverify.dev/v1/validate?...') with Authorization: Bearer <key>.

What downtime looks like

VIES hits about 99% overall, but specific country registries (ES, DE, IT) degrade noticeably during EU business hours. The MS_UNAVAILABLE error code is the VIES-native signal. Scheduled maintenance typically runs Monday mornings 03:00 to 05:00 UTC. Country-specific registries can lag or become unavailable for hours at a time. Spain's registry regularly hits more than 5% error rates between 09:00 to 12:00 CET. Peak-hour degradation across all EU registries is common 10:00 to 14:00 UTC. Real downtime is rare (less than 0.5%), but service degradation is part of the expected operational reality.

How vatverify handles it automatically

  • 30-day cache TTL on valid responses: most requests don't hit VIES
  • Stale-while-revalidate: when VIES returns an error and we have a cached copy, we return it with source_status: "degraded" (200 OK, not 502)
  • When there's no cache and VIES is down: we return 502 registry_unavailable with a structured error
  • Retry strategy inside vatverify: 3 attempts with exponential backoff before giving up

What your code should do

Handle three response shapes:

safe-validate.ts
import { Vatverify, VatverifyError } from '@vatverify/node';

const vat = new Vatverify(process.env.VATVERIFY_API_KEY!);

async function safeValidate(vatNumber: string) {
  try {
    const result = await vat.validate({ vat_number: vatNumber });

    if (result.meta.source_status === 'degraded') {
      // Accept as valid for now, but log for reconciliation
      await logReconciliationTask(vatNumber);
      return { valid: true, confidence: 'degraded' };
    }

    return { valid: result.data.valid, confidence: 'fresh' };
  } catch (err) {
    if (err instanceof VatverifyError && err.code === 'registry_unavailable') {
      // Last resort - check format + checksum offline
      return fallbackOffline(vatNumber);
    }
    throw err;
  }
}

async function fallbackOffline(vatNumber: string) {
  const { validate } = await import('@vatverify/vat-rates');
  const result = validate(vatNumber);
  return { valid: result.valid, confidence: 'format-only' };
}

Retry with exponential backoff

For cases where you want a longer retry window (for example, a background job that can wait 30 seconds):

retry-validate.ts
async function retryValidate(vatNumber: string, maxRetries: number = 3) {
  let delay = 250;
  for (let i = 0; i < maxRetries; i += 1) {
    try {
      return await vat.validate({ vat_number: vatNumber });
    } catch (err) {
      if (err instanceof VatverifyError && err.code === 'registry_unavailable' && i < maxRetries - 1) {
        await new Promise(r => setTimeout(r, delay));
        delay *= 2;
        continue;
      }
      throw err;
    }
  }
}

Note: vatverify already retries internally. This wrapper is for cases where you want a longer total window.

The offline fallback library

The @vatverify/vat-rates library runs offline, no network, instant. It won't tell you a VAT is registered, but it will tell you it's correctly shaped and has valid check digits. This is enough to reject obvious garbage at checkout. Use it to catch structural errors before calling the API, or as the final safety net when VIES is completely unavailable. On checkout pages, you can use offline validation for immediate user feedback while the API call completes in the background.

Monitoring your own downtime handling

  • Log the distribution of source_status values (live / cached / degraded). If degraded responses exceed 5% over an hour, investigate upstream.
  • Alert if registry_unavailable errors exceed 1% of requests over 5 minutes. This signals the upstream registry is down and your cache is being missed.
  • Track reconciliation task count. If the degraded-hit rate stays above 10% for an hour, something upstream is broken.

FAQ

How often is VIES actually down?

About 1% of requests across the whole system; individual countries see up to 5% during their business peak.

Will I be charged for degraded responses?

No. Any response served from cache (source_status: "cached" or "degraded") doesn't count against your monthly quota. Only source_status: "live" responses consume quota.

Can I force a fresh call even during downtime?

Add cache=false as a query parameter. This bypasses the 30-day cache and forces a fresh upstream lookup. When the upstream registry is down this will surface the underlying failure instead of quietly serving the cached value. Use sparingly.

Does HMRC (UK) have the same downtime?

HMRC is more reliable than VIES overall (99.9%+). Same degraded-cache pattern applies but triggers less often.

What about Swiss BFS and Norwegian brreg?

Both are very reliable (99.95%+). The degraded-cache pattern is there if needed but rarely kicks in.

Validate VAT in three lines.

Free up to 500 requests per month. No credit card.

Start free