Skip to content

Errors

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.

{
"error": {
"code": "specific_machine_readable_code",
"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.

Statuserror.codeSourceCommon causes
400validation_error / bad_requestLive-verifiedMissing required parameter (e.g. ?statement= on /financials); enum value out of range (e.g. statement=banana); malformed query
401unauthorizedLive-verifiedMissing API key, invalid key, revoked key
402tier_requiredSpec-inferred — possible, not yet observedEndpoint requires a higher pricing tier (S17 gate). Response should include current_tier and required_tier.
404not_foundLive-verifiedUnknown ticker / CIK / FIPS / industry code; or unknown route
429rate_limit_exceededLive-verifiedDaily or burst cap hit. Response includes Retry-After header.
500internal_errorSpec-inferred — verify at dev timeServer-side error. Transient — retry with exponential backoff.
503upstream_unavailableSpec-inferred — verify at dev timeUpstream data source (SEC, Census, BLS, SBA) temporarily unreachable.
error.codeCauseFix
unauthorizedMissing API key, or key is invalid / revokedGenerate a new key in the portal. Pass it via X-API-Key: <key> or Authorization: Bearer <key>. The error.message distinguishes “missing” from “invalid”.
error.codeCauseFix
validation_errorRequired 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_requestParameter 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.).Use one of the allowed values.
error.codeCauseFix
not_foundUnknown company / FIPS / industry — OR unknown route. The error.message names the resource if it’s a data lookup (e.g. Company 'ZZZZZZ' not found.).For company lookups: confirm the ticker or CIK is in the SEC EDGAR coverage. For FIPS: see Census coverage. For routes: check the API Reference.
error.codeCauseFix
tier_requiredEndpoint requires a higher pricing tier than the API key has. Response carries current_tier and required_tier.Upgrade in the portal or fall back to a free-tier endpoint. See Pricing.
error.codeCauseFix
rate_limit_exceededDaily request cap hit OR per-second burst cap hit. The error.message distinguishes the two. Response carries Retry-After (seconds).Daily: wait until midnight UTC, or upgrade tier. Burst: wait the Retry-After value (typically 1 sec) and retry.
error.codeCauseFix
internal_errorServer-side error.Retry with exponential backoff. Report persistent cases to support@thesma.dev with the response body.
upstream_unavailableUpstream data source (SEC, Census, BLS, SBA) is down.Transient — retry later.

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.

  • 4xx are your problem — don’t retry unless the cause is transient (e.g., 429).
  • 5xx are our problem — retry with exponential backoff (the Python SDK does this automatically).
  • 429 — respect Retry-After and retry after the specified number of seconds.