Errors¶
Every error, from any route including unknown ones, has the same JSON body:
code is a stable machine-readable string; message is for humans and may change. Branch on the HTTP status or on code, never on the message text. The X-Request-Id header is present on errors too; quote it when you report a problem.
Codes¶
code |
HTTP | When | Retry? |
|---|---|---|---|
bad_request |
400 | A parameter is missing, malformed or out of range | No. Fix the request. |
unauthorized |
401 | Key missing, malformed, wrong or revoked. Response has WWW-Authenticate: Bearer. |
No. Fix or replace the key. |
forbidden |
403 | The key is valid but lacks the data scope |
No. Ask for a key with the right scope. |
not_found |
404 | Unknown route, or the entity does not exist (for example a well-formed but unknown company INN) | No. |
method_not_allowed |
405 | Wrong HTTP method. REST routes accept GET only; /mcp accepts POST only. The Allow header names the right method. |
No. |
rate_limited |
429 | The key's request budget is used up. Response has Retry-After (whole seconds). |
Yes, after Retry-After seconds. |
timeout |
504 | The query ran longer than the service's 30 second limit | Once or twice, then narrow the request. |
internal |
500 | The service failed (for example the database is unavailable) | Yes, with backoff. |
GET /v1/health is the one place a 503 appears: {"data": {"ok": false, "db": false}, ...} when the database check fails.
Examples¶
400 bad_request¶
Real responses:
The second form lists every offending parameter, comma-separated. Other messages you can get from bad_request (all observed from the service): start/end must be YYYY-MM-DD; resample must be '', week, month, quarter or year; years must be between 1 and 20; give at least one filter (query, inn, okved_prefix, region, ...) for company search without any filter; query is too short or only a legal-form word ... after normalisation; add a more distinctive word ... when the only filter is a query that normalises to nothing; inn must be 10 (legal entity) or 12 (individual) digits for a malformed INN; hs must have at least 4 digits ... for a too-short HS code on /v1/restrictions/{hs}; a parameter value was rejected by the database for a value that fits the type but not the data.
Do not retry a 400 unchanged: it will fail the same way.
401 unauthorized¶
Real response, same body for a missing key and a wrong key:
See Authentication.
403 forbidden¶
The key authenticated but has no data scope. This is what the service sends:
404 not_found¶
Real responses:
Note
Not every empty result is a 404. A series id that does not exist returns 200 with an empty rows list, and /v1/latest leaves unknown ids out of rows and lists them in data.missing. Only entity lookups by key (a well-formed but unknown company INN) and unknown routes return 404; a malformed INN is a 400. See the FAQ.
405 method_not_allowed¶
A POST to a REST route, or a GET to /mcp:
The /mcp endpoint answers GET and DELETE with the message use POST and the header Allow: POST. REST routes answer with Allow: GET.
429 rate_limited¶
Retry-After is the number of whole seconds (at least 1) until one request is available again. Wait that long, then retry. Details and a backoff helper: Rate limits.
504 timeout and 500 internal¶
A 500 can also carry the message internal error for an unexpected failure. Error messages never contain SQL, connection details or server paths.
A 504 means the request asked for too much work. Retrying the identical request tends to time out again, so retry at most once or twice and then make the request smaller: a shorter date window, resample, a tighter filter. A 500 is the service's problem, not yours: retry with backoff, and if it persists, report it with the X-Request-Id.
Retry guidance¶
| Status | Retry | How |
|---|---|---|
| 400, 401, 403, 404, 405 | No | The same request will fail the same way. Fix the request or the key. |
| 429 | Yes | Sleep for Retry-After seconds, then retry. Do not retry immediately. |
| 500, 504 | Yes, limited | Exponential backoff (1 s, 2 s, 4 s ...) with jitter, three to five attempts at most. For 504, shrink the request. |
| Network errors, connection resets | Yes, limited | Same as 500. All REST calls are GET and safe to repeat. |
A minimal Python implementation of this table is in Rate limits.
Errors over MCP¶
On /mcp, authentication, scope, rate-limit and method errors are plain HTTP responses with the bodies above. Errors inside a tool call (bad arguments, timeouts) come back as a normal MCP result whose text is {"error": {"code": ..., "message": ...}}, with the same code values.