Skip to content

Errors

All REST errors return a JSON body. Most are the simple shape {"error": "<message>"}; the two 429 variants carry extra fields (below).

Status When Example body
400 Invalid request — missing/invalid fields, malformed JSON, bad scope combination {"error": "query is required"}
401 Missing or invalid API key {"error": "API key required"}
403 Key revoked/disabled/expired, account disabled, missing entitlement, or cross-company search not enabled {"error": "API key has expired"}
404 Company not found {"error": "Unknown ticker: ZZZZ"}
429 Rate limit or quota exceeded see below
500 Unexpected server error {"error": "Facts lookup failed"}
503 Backend temporarily unavailable {"error": "Facts backend unavailable"}

Common messages, verbatim:

  • Request body must be valid JSON / Request body must be a JSON object
  • query is required · query must be at least 2 characters
  • ticker/cik and tickers/ciks are mutually exclusive
  • Facts lookup currently requires a single company scope via ticker or cik
  • series_key values must be stable sha256 hex strings
  • Maximum 100 series_key values per facts lookup
  • Facts-by-series lookup requires series_key or series_keys
  • after must be YYYY-MM-DD (or 1y/3y/5y/all) · before must be YYYY-MM-DD
  • Cross-company search requires at least 2 terms
  • ids is required · Maximum 100 series IDs per batch
  • evidence_key is required
  • Unknown ticker: ZZZZ (or Unknown tickers: … for sets)
  • Company not found
  • ticker and cik do not match is a 400 (both were sent and disagree)
{
"error": "Rate limit exceeded",
"retryAfter": 31,
"limit": 60,
"windowMs": 60000
}

Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After. Wait retryAfter seconds and retry.

{
"error": "monthly quota exceeded",
"quota": "monthly",
"scope": "account",
"limit": 5000,
"used": 5000
}

quotadaily | monthly; scopeaccount | api_key. Account quotas sum usage across all of the account’s keys. Resolve by waiting for the period to reset or upgrading. See Rate limits & quotas for full behavior.

The keyless sandbox returns a 429 with error code SANDBOX_LIMIT when your IP has used its 5 daily requests. Check the X-Sandbox-Remaining header on every sandbox response.

The verified-facts store is served by a dedicated backend. When it’s unreachable you get 503 with messages like Facts backend unavailable, Evidence backend unavailable, Series catalog backend unavailable, or Search storage not available. This is transient infrastructure, never a data gap — retry with exponential backoff. Filing/entity search degrade gracefully to a fallback index before ever returning 503.

MCP tool failures are returned inside the JSON-RPC result with isError: true and body { "error": { "code", "message", … } }:

Code Meaning Recovery
UNKNOWN_TICKER Company not found Check the symbol
METRIC_NOT_FOUND Nothing matched the metric query Use the returned suggestions
METRIC_AMBIGUOUS Several plausible series Pick from candidates, call again with its metric_id
NO_DATA_FOR_PERIOD Metric exists, period filter excluded everything Relax period_type/fiscal_year
SERIES_NOT_FOUND Unknown/empty series Re-run search_facts
EVIDENCE_NOT_FOUND Evidence ID/URL didn’t resolve Use a fresh evidence_url
UPSTREAM_UNAVAILABLE Transient backend outage Retry shortly
INTERNAL_ERROR Unexpected failure Retry; contact support if persistent

Protocol-level problems (unknown tool, invalid arguments, malformed JSON-RPC) use standard JSON-RPC error codes (-32600, -32601, -32602, -32700).