Home / Use cases / Node.js

Node.js

VAT API Rate Limiting & Caching: Production Best Practices

Optimise VAT API usage with effective caching and rate limit management. Covers in-memory cache, Redis distributed cache, request deduplication, quota tracking, and graceful 429 handling.

Every TaxID plan has a monthly validation quota. Burning through quota faster than necessary is expensive and avoidable. The single most effective optimisation is caching: a VAT number that returns 'active' today will almost certainly return 'active' again tomorrow — VIES itself only updates in real time, but registrations rarely change day-to-day. A 23-hour cache window (matching VIES's own cache TTL) eliminates the vast majority of redundant API calls in any application where the same customers validate repeatedly.

For single-instance applications, an in-memory Map or a library like node-lru-cache is sufficient. The limitation is that the cache is lost on process restart and is not shared across multiple instances in a horizontally scaled deployment. For production applications running on multiple servers or in serverless environments, use a shared Redis cache with a TTL matching the 23-hour window.

Request deduplication is the second key optimisation. If your application can generate concurrent validation requests for the same VAT number (for example, a user who submits the checkout form twice in quick succession, or a batch job that processes the same vendor multiple times), add a deduplication layer that detects in-flight requests for the same key and waits for the first result rather than making a parallel duplicate API call.

Rate limit handling requires defensive coding for 429 responses. When your application receives a 429, back off exponentially — wait 1 second, then 2, then 4 — before retrying. Track your monthly usage by counting validation calls in your own database. The TaxID API does not expose a usage endpoint, so your only reliable view of quota consumption is what your application has tracked.

For serverless environments (Vercel Edge, Cloudflare Workers, AWS Lambda), in-memory caching is unreliable because each function invocation may run in a separate process. Always use an external cache store in serverless contexts. Upstash Redis is a good choice — it has a REST API that works from any serverless runtime including Edge functions that cannot use native TCP connections.

Implementation steps

  1. 1

    Add in-memory caching for single-instance apps

    Create a Map-based cache in your VatValidator service class. On each validation call, check the cache for a recent result (under 23 hours old) before calling the API. Only cache 'active' and 'inactive' results — do not cache 'service_unavailable', as the registry may recover before your 23-hour TTL expires.

  2. 2

    Upgrade to Redis for multi-instance deployments

    Replace the in-memory Map with an Upstash Redis client (REST API works in serverless). Use setex with a TTL of 82800 seconds (23 hours). Use the normalised VAT number (uppercase, spaces stripped) as the cache key prefixed with 'vat:'. The TaxID API marks cached responses with cached: true — combine your Redis cache hit with the API's own caching for maximum efficiency.

  3. 3

    Implement request deduplication

    Add a pendingRequests Map that stores in-flight Promise<VatResult> objects keyed by the normalised VAT number. Before making an API call, check if a request is already in flight for the same key. If so, return the existing Promise instead of making a new API call. Remove the key from the map when the Promise resolves (in both success and error cases).

  4. 4

    Track quota usage

    Count every outgoing TaxID API call (not cache hits) in a database counter. Use a monthly bucket key (e.g., 'vat_calls:2026-06') and increment it with each real API call. Alert your team when you reach 80% of your plan limit. This gives you time to upgrade or investigate unexpected usage spikes before hitting the hard limit.

  5. 5

    Handle 429 with exponential backoff

    Wrap your API call in a retry loop that detects HTTP 429 responses and backs off exponentially: wait 1000ms, then 2000ms, then 4000ms, for a maximum of 3 retries. Log every 429 with the affected VAT number, current timestamp, and retry attempt number. If all retries exhaust, return service_unavailable — never throw an unhandled error to the caller for rate limit responses.

Code example

Node.js

const res = await fetch(
  'http://localhost:3000/api/v1/validate/DE/DE123456789',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { valid, status, company_name, company_address } = await res.json();

if (valid) {
  console.log(`Valid EU business: ${company_name}`);
} else if (status === 'service_unavailable') {
  // VIES is temporarily down — retry or allow with manual check
  console.log('VIES unavailable — check back in a few minutes');
} else {
  console.log('Invalid VAT number — charge local tax rate');
}

Python

import requests

res = requests.get(
    "http://localhost:3000/api/v1/validate/DE/DE123456789",
    headers={"Authorization": "Bearer YOUR_API_KEY"}
)
data = res.json()

if data["valid"]:
    print(f"Valid: {data['company_name']}")
elif data["status"] == "service_unavailable":
    print("VIES temporarily unavailable")
else:
    print("Invalid VAT number")

API response

The TaxID API returns a consistent JSON response for every validation request:

200 OK (active)valid
{
  "valid": true,
  "status": "active",
  "country_code": "DE",
  "vat_number": "123456789",
  "company_name": "Example GmbH",
  "company_address": "Musterstraße 1, 10115 Berlin",
  "request_date": "2026-05-10T00:00:00.000Z",
  "cached": false,
  "request_id": "req_01j..."
}

Error handling

The API uses a consistent Stripe-style error format. Always handle service_unavailable separately — VIES has occasional downtime and you should not reject valid customers during outages.

active

VAT number is valid and the business is registered

invalid

VAT number format is wrong or not registered in VIES

service_unavailable

VIES or the national system is temporarily down — retry later

Frequently asked questions

How long should I cache VAT validation results?

23 hours is the recommended maximum — this matches VIES's own internal cache window. VIES only guarantees real-time status at the point of the query; caching for 23 hours is considered compliant for EU VAT purposes. For high-risk transactions (high-value invoices or new customers), re-validate immediately rather than relying on cache.

Should I cache 'service_unavailable' responses?

No. A service_unavailable response means the registry is temporarily offline — caching it would prevent you from getting the real status once the registry recovers. Only cache 'active' and 'inactive' results, which represent definitive answers from the national registry.

What happens when I hit the 429 rate limit?

The TaxID API returns HTTP 429 with a Retry-After header indicating when the rate limit window resets. Implement exponential backoff as described in the steps above. For batch jobs, spread requests over time using a queue rather than parallel execution — this prevents spikes that trigger rate limiting.

Further reading

Evaluating EU VAT APIs? Compare TaxID with:

Ready to implement?

Get your free API key and start validating EU VAT numbers today.

Get free API key

Related use cases

Stripe EU VAT: Validate Tax IDs Before Charging Customers

Stripe EU VAT integration guide: validate EU VAT numbers server-side before applying zero-rate B2B e...

UK VAT validation in Shopify B2B

Validate UK VAT numbers for B2B customers in Shopify. Required under UK Making Tax Digital rules for...

WooCommerce Spain NIF/CIF validation

Validate Spanish NIF and CIF numbers in WooCommerce checkout. Automatically apply B2B tax exemptions...