Errors and status codes
Every error response is the same JSON shape. The HTTP status tells you broad class; the error type tells you the specific cause.
The envelope
Most error responses return a single detail string:
{
"detail": "since must be ISO8601 (e.g. 2026-04-29T01:23:45Z)"
}The exception is 429 Too Many Requests, which returns a richer flat object with fields for programmatic handling (see Rate limits):
{
"type": "rate_limit_exceeded",
"limit": "burst",
"message": "Burst rate exceeded. Try again in 12 seconds.",
"request_id": "req_8jJw3Fk1pQzN",
"documentation_url": "https://vera.cryptobriefing.com/docs/rate-limits"
}For 429 responses, include the request_id when contacting support. For all other errors, include the detail string and the HTTP status code.
Status codes
| Code | Meaning | Retry? |
|---|---|---|
| 200 | OK | n/a |
| 400 | Bad request: malformed JSON, invalid parameter | No, fix the request |
| 401 | Authentication failed: missing, invalid, or revoked key | No, rotate or fix the key |
| 402 | Payment required: subscription past_due, unpaid, or trialing-but-suspended | No, resolve billing |
| 403 | Forbidden: your key lacks the required scope for this endpoint | No, request a key with the scope |
| 404 | Not found: unknown event id, unknown market id, unknown endpoint | No |
| 409 | Conflict: incompatible WebSocket subscription request | No, fix the message |
| 422 | Unprocessable: request is well-formed but semantically wrong (e.g. since > until) | No, fix the request |
| 429 | Too many requests: rate limit exceeded | Yes, honour Retry-After |
| 500 | Unexpected server error | Yes, exponential backoff |
| 502 / 503 / 504 | Upstream or gateway issue, briefly | Yes, exponential backoff |
Common detail messages
| Status | detail | Notes |
|---|---|---|
| 401 | Missing API key. Pass X-API-Key header or ?api_key= param. | No recognised credential on the request. |
| 401 | Invalid or revoked API key. | Key not recognised or was revoked. Rotate if you suspect leakage. |
| 400 | since must be ISO8601 (e.g. 2026-04-29T01:23:45Z) | A query parameter failed validation. The message names the parameter. |
| 404 | Not Found | Resource or endpoint does not exist. |
| 429 | (richer object, see above) | Rate limit exceeded. Honour Retry-After. The limit field tells you which limit was hit. |
Retry strategy
Retry only on 429 and 5xx. Never retry on 4xx other than 429. Those are deterministic and will return the same error.
For 429, honour Retry-After verbatim. For 5xx, use exponential backoff with jitter (1s, 2s, 4s, 8s, 16s; randomise ±20%). Cap at 5 retries.
async function fetchWithRetry(url: string, opts: RequestInit, maxAttempts = 5): Promise<Response> { for (let attempt = 0; attempt < maxAttempts; attempt++) { const res = await fetch(url, opts); if (res.status < 400) return res; if (res.status >= 400 && res.status < 500 && res.status !== 429) return res; // non-retryable if (res.status === 429) { const wait = Number(res.headers.get("Retry-After") ?? 1) * 1000; await new Promise((r) => setTimeout(r, wait)); continue; } // 5xx: exponential backoff with jitter const base = 1000 * Math.pow(2, attempt); const jitter = base * 0.2 * (Math.random() - 0.5) * 2; await new Promise((r) => setTimeout(r, base + jitter)); } throw new Error("Vera: max retries exceeded"); }
Reporting a bug
If you see a response you can't reconcile against this page, email vera@cryptobriefing.com with the request_id and the request you sent (redact the bearer token). For quick questions, our Discord is the fastest way to reach us.