Errors
HTTP status codes
Section titled “HTTP status codes”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"} |
400 — validation
Section titled “400 — validation”Common messages, verbatim:
Request body must be valid JSON/Request body must be a JSON objectquery is required·query must be at least 2 charactersticker/cik and tickers/ciks are mutually exclusiveFacts lookup currently requires a single company scope via ticker or cikseries_key values must be stable sha256 hex stringsMaximum 100 series_key values per facts lookupFacts-by-series lookup requires series_key or series_keysafter must be YYYY-MM-DD (or 1y/3y/5y/all)·before must be YYYY-MM-DDCross-company search requires at least 2 termsids is required·Maximum 100 series IDs per batchevidence_key is required
404 — unknown company
Section titled “404 — unknown company”Unknown ticker: ZZZZ(orUnknown tickers: …for sets)Company not foundticker and cik do not matchis a400(both were sent and disagree)
429 — rate limit
Section titled “429 — rate limit”{ "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.
429 — quota
Section titled “429 — quota”{ "error": "monthly quota exceeded", "quota": "monthly", "scope": "account", "limit": 5000, "used": 5000}quota ∈ daily | monthly; scope ∈ account | 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.
429 — sandbox
Section titled “429 — sandbox”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.
503 — backend unavailable
Section titled “503 — backend unavailable”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 errors
Section titled “MCP tool errors”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).