> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avatcado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching

> How Avatcado caches validation results

# Caching

Avatcado 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:

```json theme={null}
{
  "data": { "..." },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "request_duration_ms": 8,
    "cached": true,
    "cached_at": "2026-03-06T10:00:00Z"
  }
}
```

| Field            | Value              | Meaning                                                          |
| ---------------- | ------------------ | ---------------------------------------------------------------- |
| `meta.cached`    | `true`             | Result served from cache                                         |
| `meta.cached`    | `false`            | Fresh result from upstream (VIES, HMRC, BFS, Bronnysund, or ABR) |
| `meta.cached_at` | ISO 8601 timestamp | When 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), Avatcado 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:

```json theme={null}
{
  "data": { "..." },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "request_duration_ms": 12,
    "cached": true,
    "cached_at": "2026-03-01T10:00:00Z",
    "stale": true
  }
}
```

| Scenario                                   | `meta.cached` | `meta.stale` |
| ------------------------------------------ | ------------- | ------------ |
| Fresh cache hit (within 25-day TTL)        | `true`        | not present  |
| Stale fallback (beyond TTL, upstream down) | `true`        | `true`       |
| Fresh upstream result                      | not present   | not present  |
| Force refresh (`cache=false`)              | not present   | not present  |

Stale results still count toward your monthly quota because you received data. If no cached result exists at all for the requested VAT number, the API returns a `503` error and that request is **automatically refunded**. See [Non-billable failures](/rate-limits#non-billable-failures) for details.

## Source status

When Avatcado fetches from an upstream service, the response includes `meta.source_status` to communicate the reliability of the result:

| Value           | Meaning                                                                                                                                                                                   |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"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`.

```json theme={null}
{
  "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:

<CodeGroup>
  ```bash curl theme={null}
  curl -H "Authorization: Bearer avat_live_your_api_key" \
    "https://api.avatcado.com/v1/validate?vat_number=NL123456789B01&cache=false"
  ```

  ```typescript @avatcado/node theme={null}
  const { data, error } = await avatcado.vat.validate({
    vatNumber: "NL123456789B01",
    cache: false,
  });
  ```

  ```python Python theme={null}
  result = avatcado.vat.validate("NL123456789B01", cache=False)
  ```
</CodeGroup>

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.

```json theme={null}
{
  "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.
