Every error — from auth failures to validation errors to 429s — follows the same nested JSON shape. Client code can pattern-match on error.code rather than parsing HTTP status alone.
"message": "Human-readable description of what went wrong.",
"status": 400
}
}
error — an object containing the structured error.
error.code — machine-readable code for programmatic handling. Stable across versions. Pattern-match on this in client code.
error.message — human-readable description. Safe to show to developers; not for end users.
error.status — HTTP status as an integer. Mirrors the response status code.
Some endpoints add additional fields under error for capability-flavored responses (e.g. tier-gated 402s on screener endpoints carry current_tier and required_tier). Always read error.code first; treat extras as best-effort context.
Generate a new key in the portal. Pass it via X-API-Key: <key> or Authorization: Bearer <key>. The error.message distinguishes “missing” from “invalid”.
Required parameter missing. The error.message names the field (e.g. field 'query -> statement': Field required).
Read the message — it tells you which field. Common cases: ?statement= on /financials, ?period= where required.
bad_request
Parameter value not in allowed set. The error.message lists valid values (e.g. Invalid statement type 'banana'. Must be one of: income, balance-sheet, cash-flow, all.).
Even on 500s the response is JSON with the above shape — the API never returns a bare HTML error page. If you get one, it’s from a layer in front of the API (your proxy, a load balancer, a caching CDN) and worth investigating there.