Errors & Status Codes

The finlight API uses conventional HTTP status codes to signal the outcome of a request. A 2xx status means success; 4xx means the request was rejected (usually something you can fix); 5xx means a problem on our side. Every error returns a JSON body describing what went wrong.

Note: REST and WebSocket requests authenticate with the X-API-KEY header. A missing or invalid key returns 401. (Webhook delivery auth — the headers finlight sends to your endpoint — is separate; see Webhook Authentication.)

Status codes at a glance

StatusMeaningWhen it happens
200OKSuccessful request.
400Bad RequestInvalid parameters or malformed query syntax.
401UnauthorizedMissing or invalid X-API-KEY.
403ForbiddenYour plan doesn't include the requested capability.
404Not FoundThe requested resource doesn't exist (e.g. unknown article link).
422Unprocessable EntityRequest body/params failed validation (global validator).
429Too Many RequestsMonthly quota or burst rate limit exceeded.
500Internal Server ErrorUnexpected error on finlight's side.

400 — Bad Request

Returned when query parameters or the request body fail validation, or when the query string uses invalid field-level syntax.

Invalid parameter:

{
  "statusCode": 400,
  "message": ["pageSize must not be greater than 100"],
  "error": "Bad Request"
}

Invalid query syntax (POST /v2/articles):

{
  "statusCode": 400,
  "message": [
    {
      "property": "query",
      "constraints": { "query": "Invalid query syntax near 'ticker:'" }
    }
  ],
  "error": "Bad Request"
}

See Advanced Query Building for the correct query syntax.


401 — Unauthorized

The X-API-KEY header is missing, malformed, or not a valid key.

{
  "statusCode": 401,
  "message": "Unauthorized"
}

Grab or rotate your key in the finlight dashboard.


403 — Forbidden

The request is authenticated, but your plan doesn't include the requested capability — for example, requesting full article content on a plan that doesn't include it.

{
  "statusCode": 403,
  "message": "Your subscription does not allow calling the extended article content"
}

See Rate Limits & Quotas for which capabilities each plan includes.


404 — Not Found

The requested resource doesn't exist — most commonly GET /v2/articles/by-link with a link finlight hasn't indexed.

{
  "statusCode": 404,
  "message": "Article not found"
}

422 — Unprocessable Entity

Some endpoints validate through the global validator, which returns a field-keyed error map. Each key is the offending field; the value lists the failed constraints.

{
  "status": 422,
  "errors": {
    "language": "language must be a string"
  }
}

Note: Validation failures may surface as 400 (on the article endpoints) or 422 (global validator), depending on the route — both indicate a malformed request. Inspect the body to see which field failed.


429 — Too Many Requests

You've exceeded a usage limit. There are two distinct cases, each with a specific message:

Monthly request quota exceeded:

{
  "statusCode": 429,
  "message": "Throttling Exception: Exceeded token limit of 5000 for the current period."
}

Burst (short-term) rate exceeded:

{
  "statusCode": 429,
  "message": "Throttling Exception: Exceeded rate limit of 10 requests per 10 seconds."
}

Note: finlight does not currently return Retry-After or rate-limit headers. For a burst 429, back off and retry after ~10 seconds. For a monthly quota 429, requests will succeed again at the start of your next billing period (or after an upgrade). See Rate Limits & Quotas.


500 — Internal Server Error

An unexpected error occurred on finlight's side. These are transient — retry with backoff, and if it persists, reach out via Support.

{
  "statusCode": 500,
  "message": "Internal server error"
}

Handling errors well

  • Always check the status code first, then read the JSON body for specifics.
  • 4xx (except 429) are your bug — fix the request; retrying unchanged won't help.
  • 429 and 5xx are retryable — use exponential backoff. For burst 429, ~10s is enough.
  • WebSocket errors arrive as an error server message on the socket rather than an HTTP status — see WebSocket Basics.
  • Webhook delivery failures (to your endpoint) are tracked in the dashboard, not returned to you synchronously — see Testing Webhooks.