Reference
Errors & Quotas
HTTP status codes, quota tracking headers, and how to handle rate limiting.
HTTP status codes
| Status | Meaning | Action |
|---|---|---|
| 200 | Success | Parse the response body. |
| 400 | Bad request | Check the request format — postcode or registration plate missing or malformed. |
| 401 | Unauthorised | Check that the X-Api-Key header is present and correct. |
| 403 | Forbidden | Your plan does not include this feature (e.g. bulk on Free tier). |
| 429 | Too many requests | Quota exhausted or rate limit hit. See Retry-After header. |
| 500 | Server error | Transient. Retry with exponential backoff. |
Quota headers
Every successful response includes these headers. Use them to track usage in real time without polling the account endpoint.
| Header | Value |
|---|---|
| X-Quota-Limit | Monthly request limit, or "unlimited" for admin accounts |
| X-Quota-Used | Requests used this period |
| X-Quota-Remaining | Remaining requests (min 0), or "unlimited" |
| X-Quota-Grace-Limit | Hard ceiling — limit × 1.1 on paid plans. Equals limit on the free tier (no grace buffer). |
| X-Quota-Resets | ISO 8601 datetime when the quota period resets |
| X-Payment-Status | "past_due" when payment has failed — action required |
| Retry-After | Seconds to wait before retrying. Present on 429 responses and on 200 responses where enrichmentPending is true. |
Quota exhaustion
When your monthly quota is reached, requests are blocked until the period resets. Paid plans include a 10% grace buffer above the stated quota — intended for genuine burst traffic near period end, not sustained overuse. When you first enter the grace zone, Zyfy will email you so you can upgrade before requests are blocked. The free tier has no grace buffer and hard-blocks at exactly 100 requests.
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
{
"error": "Monthly request limit reached.",
"limit": 100,
"used": 330,
"resets": "2026-04-01T00:00:00Z"
} The resets field tells you when the quota period resets. Upgrade your plan for a higher quota.
Rate limiting
In addition to the monthly quota, requests are rate-limited per minute. The limit depends on your plan:
| Plan | Rate limit |
|---|---|
| Free | 10 req / min |
| Micro | 30 req / min |
| Starter | 60 req / min |
| Pro | 300 req / min |
| Business | 600 req / min |
| Enterprise | 600 req / min |
HTTP/1.1 429 Too Many Requests
Retry-After: 10Enrichment pending
This is not an HTTP error — the response is 200 OK. When a
vehicle registration is not yet in our database, Zyfy returns a provisional response immediately
with enrichmentPending: true and signal fields set to null. Enrichment runs in the background and typically completes
within a few seconds.
What to do
- Check
enrichmentPendingin the response. - If
true, read theRetry-Afterheader and wait that many seconds. - Retry the same request — enrichment typically completes within seconds.
- Give up after 5 attempts. Each retry counts as one quota request.
Handling errors and pending enrichment
A robust client handles both 429 rate-limit responses and enrichmentPending: true in the same retry loop.
async function lookupVehicle(reg, maxAttempts = 5) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const res = await fetch(`https://zyfy.uk/v1/vehicle/${reg}`, {
headers: {
'X-Api-Key': process.env.ZYFY_API_KEY,
},
});
// Handle rate limiting — Retry-After tells you how long to wait
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get('Retry-After') ?? '60', 10);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
if (!res.ok) throw new Error(`Zyfy error: ${res.status}`);
const data = await res.json();
// Handle enrichment pending — Retry-After is also set on these 200 responses
if (data.enrichmentPending) {
const retryAfter = parseInt(res.headers.get('Retry-After') ?? '10', 10);
if (attempt < maxAttempts) await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
return data;
}
throw new Error('Did not complete in time — try again shortly');
}Error response shape
All error responses return a JSON body with an error string:
See also
- → Quickstart — from zero to your first API call
- → Postcode API — postcode endpoint reference
- → Vehicle API — vehicle endpoint reference
- → Pricing — compare plans and quota limits