Documentation · Errors

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:

jsonjson
{
  "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):

jsonjson
{
  "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

CodeMeaningRetry?
200OKn/a
400Bad request: malformed JSON, invalid parameterNo, fix the request
401Authentication failed: missing, invalid, or revoked keyNo, rotate or fix the key
402Payment required: subscription past_due, unpaid, or trialing-but-suspendedNo, resolve billing
403Forbidden: your key lacks the required scope for this endpointNo, request a key with the scope
404Not found: unknown event id, unknown market id, unknown endpointNo
409Conflict: incompatible WebSocket subscription requestNo, fix the message
422Unprocessable: request is well-formed but semantically wrong (e.g. since > until)No, fix the request
429Too many requests: rate limit exceededYes, honour Retry-After
500Unexpected server errorYes, exponential backoff
502 / 503 / 504Upstream or gateway issue, brieflyYes, exponential backoff

Common detail messages

StatusdetailNotes
401Missing API key. Pass X-API-Key header or ?api_key= param.No recognised credential on the request.
401Invalid or revoked API key.Key not recognised or was revoked. Rotate if you suspect leakage.
400since must be ISO8601 (e.g. 2026-04-29T01:23:45Z)A query parameter failed validation. The message names the parameter.
404Not FoundResource 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.

retry.tsts
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.