Skip to content

Error catalog

Every error uses the same body shape and a stable machine-readable code, so you can handle failures the same way across every endpoint.

The error body

Failures return HTTP status plus a JSON body with a stable status: "error", a request_id for support correlation, a timestamp, and an error object with a code, a human message, and optional details. Branch on error.code, never on the message text (messages may be reworded; codes are stable). One legacy exception: the token-quota 402 uses a flat body where error is a string, so detect it by status 402 — see The 402 exception below.

error body · json
{
  "status": "error",
  "request_id": "b3f1c2a0-7d4e-4a11-9c8e-2f0a1b6d5e77",
  "timestamp": "2026-07-16T06:12:44.001Z",
  "error": {
    "code": "INVALID_PARAM",
    "message": "period must be one of A, Q, TTM",
    "details": null
  }
}

Some errors carry a pointer to a next step inside error.details or in the message — for instance, a not-yet-covered company points you at the coverage endpoint and a fallback. Successful responses use the mirror shape: status: "success" with data and the same request_id.

Error codes

HTTPerror.codeWhen it happens
400INVALID_PARAMA parameter is missing, malformed, or has an unaccepted value. The message names the offending parameter.
401UNAUTHORIZEDNo API key was supplied, or the key is invalid. Send your key as an X-API-Key header or ?apiKey= query parameter.
402error: "insufficient_tokens"Your token balance cannot cover this call. Top up on the pricing page. Failed calls (4xx/5xx) are auto-refunded. NOTE: this is the one error that does NOT use the envelope below — see "The 402 exception".
404NOT_FOUNDThe requested resource does not exist — for example an accession or filename that is not present.
404NOT_COVEREDA real company whose standardized financials are not yet available. The response includes a pointer to the coverage endpoint and a fallback. (A typo’d ticker vs a real-but-unprocessed company are being split into distinct codes.)
405METHOD_NOT_ALLOWEDThe HTTP method is not supported on this path (e.g. POST to a GET-only endpoint).
429RATE_LIMIT_EXCEEDEDYou exceeded a rate limit. Whether the response carries a Retry-After header depends on which limiter tripped — see the panel below.
500INTERNAL_ERRORAn unexpected server error. The request_id lets support correlate it in the logs. Token cost is auto-refunded.

The 402 exception

One error does not use the envelope above: the token-quota 402. It returns a flat body where error is a string — there is no error.code, request_id, or timestamp on it. Detect it by the status code 402 together with error === "insufficient_tokens", not by error.code:

402 body (flat — legacy shape) · json
{
  "error": "insufficient_tokens",
  "current_balance": 0,
  "required_cost": 25,
  "next_refill_at": "2026-08-01T00:00:00Z",
  "plan": "free",
  "upgrade_url": "https://finradar.ai/pricing"
}

Why it is different

The 402 quota body predates the standard error envelope and stays as-is for backward compatibility. Every other error on this page uses the enveloped shape with a stable error.code; only the 402 carries the balance / cost / refill fields inline so a client can show the shortfall and the upgrade link directly.

Rate limits and Retry-After — the honest version

Every over-limit response is HTTP 429 with error.code = "RATE_LIMIT_EXCEEDED". Whether it also tells you how long to wait depends on which limiter tripped:

  • App-wide default limiter — its 429 does carry Retry-After plus X-RateLimit-Limit / Remaining / Reset. Back off using those.
  • Gateway-enforced families — Company Financials and XBRL financial statements are rate-limited by IP at the gateway. A gateway 429 carries neither Retry-After nor X-RateLimit-* headers. Use a fixed backoff for these.
  • Login lockout — the brute-force protection on the login endpoint returns a 429 that does carry Retry-After.

Do not assume Retry-After is always present

If your client blindly reads Retry-After on every 429, it will read nothing on the gateway-enforced families. Treat a missing header as “use my own backoff.”

The exact per-family limits and enforcement layers are on the API Policies page, and are also served machine-readably at /api/v1/policy.