Skip to main content

Caching

Vatly caches validation results so repeat lookups return faster and your integration stays resilient even when upstream services (VIES, HMRC, BFS, Bronnysund, or ABR) are down.

How it works

  • TTL: Validation results are cached for 25 days
  • Cache key: The combination of the VAT number and the requester VAT number (if provided)
  • Storage: Cached in the database alongside fresh results

Detecting cached responses

When a result comes from cache, two fields appear in the meta object:
{
  "data": { "..." },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "request_duration_ms": 8,
    "cached": true,
    "cached_at": "2026-03-06T10:00:00Z"
  }
}
FieldValueMeaning
meta.cachedtrueResult served from cache
meta.cachedfalseFresh result from upstream (VIES, HMRC, BFS, Bronnysund, or ABR)
meta.cached_atISO 8601 timestampWhen the cached result was originally fetched

Usage counting

Cached responses still count toward your monthly quota. This is because caching is a performance optimization, not a billing bypass. Check your remaining quota via the X-RateLimit-Remaining header on each response.

Stale cache fallback

When an upstream service (VIES, HMRC, BFS, Bronnysund, or ABR) is down and no fresh cache exists (within the 25-day TTL), Vatly falls back to the most recent cached result for that VAT number, regardless of age. The response includes meta.stale: true so you know the data may be outdated:
{
  "data": { "..." },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "request_duration_ms": 12,
    "cached": true,
    "cached_at": "2026-03-01T10:00:00Z",
    "stale": true
  }
}
Scenariometa.cachedmeta.stale
Fresh cache hit (within 25-day TTL)truenot present
Stale fallback (beyond TTL, upstream down)truetrue
Fresh upstream resultnot presentnot present
Force refresh (cache=false)not presentnot present
Stale results still count toward your monthly quota. If no cached result exists at all for the requested VAT number, the API returns a 503 error as usual.

Source status

When Vatly fetches from an upstream service, the response includes meta.source_status to communicate the reliability of the result:
ValueMeaning
"live"Fresh result confirmed by the upstream authority
"unavailable"The VIES member state was down. A stale cached result was served instead
"degraded"VIES returned valid: false but a prior lookup found this number valid. The result may be a silent false negative from a degraded member state. A stale cached result was served instead
When source_status is "unavailable" or "degraded", the response also includes meta.stale: true and meta.cached: true.
{
  "data": { "valid": true, "vat_number": "FR59542051180", "..." },
  "meta": {
    "request_id": "...",
    "cached": true,
    "cached_at": "2026-03-01T10:00:00Z",
    "stale": true,
    "source_status": "degraded"
  }
}
Cache hits do not include source_status. The field only appears when an upstream call was attempted. If no stale cache exists when a member state is unavailable, the API returns a 503 error with code upstream_member_state_unavailable.

Non-EU validations

CH, LI, NO, and AU validations follow the same 25-day cache policy as EU and UK validations. Caching is especially important for Swiss and Liechtenstein validations because the BFS UID Register enforces a strict 20 requests per minute rate limit. Cached results help you avoid hitting this upstream limit.

Force refresh

Pass cache=false to bypass the cache and fetch a fresh result from the upstream service:
curl -H "Authorization: Bearer vtly_live_your_api_key" \
  "https://api.vatly.dev/v1/validate?vat_number=NL123456789B01&cache=false"
The fresh result is written to cache, so subsequent requests (without cache=false) will use it for the next 25 days. Force-refresh requests count toward your monthly quota like any other request. If the upstream service is unavailable during a force-refresh, the API falls back to the most recent cached result (with meta.stale: true), the same as a normal request.

Batch caching

In batch responses, cache information appears per-item in each result’s meta object. Each item in a batch may have a different cache state. One may be a fresh lookup while another is served from cache.
{
  "data": {
    "results": [
      {
        "data": { "valid": true, "vat_number": "NL123456789B01", "..." },
        "meta": { "cached": true, "cached_at": "2026-03-06T10:00:00Z" }
      },
      {
        "data": { "valid": true, "vat_number": "DE987654321", "..." },
        "meta": {}
      }
    ],
    "summary": { "total": 2, "succeeded": 2, "failed": 0 }
  },
  "meta": { "request_id": "...", "request_duration_ms": 350 }
}
The cache parameter in the request body applies to all items in the batch. Setting cache: false forces fresh lookups for every VAT number.