market
Errors
One table for the whole API. Every endpoint answers with the same statuses, the same codes
and the same message_keys, with two endpoint-specific parts: which query parameter can earn
you a 400 (the BAD_REQUEST row says which is which), and the 404 row, which only
/global can return.
Every error carries the same envelope as a success response, with result set to {}:
{
"code": 40100,
"message": "the supplied API key is missing or unknown",
"message_key": "INVALID_API_KEY",
"result": {}
}
| HTTP status | code |
message_key |
Cause |
|---|---|---|---|
| 401 | 40100 | INVALID_API_KEY |
X-Api-Key header missing, or the key is unknown. |
| 401 | 40101 | API_KEY_EXPIRED |
The key's expiry date has passed. Contact us for a new one — nothing about your request was wrong. |
| 401 | 40102 | API_KEY_REVOKED |
The key was revoked or disabled. It will not start working again on its own — ask us before retrying. |
| 400 | 40000 | BAD_REQUEST |
A query parameter failed validation. On /assets and /quotes: a non-integer page/size/limit/offset, page < 1, offset < 0, size or limit above 100, a value in id that is not a non-negative integer, more than 100 values in id/symbol/slug — or in fields, which only /assets reads — a sort naming a key outside id/name/symbol/slug or a direction other than asc/desc, or written in any form but <key>[:asc|desc] once surrounding whitespace is trimmed (name desc and name::desc included), or an offset (or the offset a page implies) above 100000. The limit and offset bounds apply in page mode too, although page mode does not use them. On /exchanges: the same, except the sort keys are id/name/slug/volume — sort=symbol is a 400 there, and ?symbol= is ignored rather than rejected. On /categories and /chains: the same as /exchanges, except the sort keys are id/name/slug/market_cap/volume_24h — sort=volume is a 400 there, and so is sort=display_name on /chains. On /trending: a by value other than gainers, the only parameter it reads. The response does not name the offending parameter — check it against that endpoint's parameter table. |
| 404 | 40400 | NOT_FOUND |
/global only. We have not produced a market-wide snapshot yet — the upstream job that writes them has not run against this database. It is not a wrong path and not a server fault: the URL is right and there is simply nothing to return. Retrying will work once the snapshot exists; if it persists, tell us with the X-Request-Id. No other endpoint returns this code — a 404 from them is the text/plain one described below. |
| 429 | 42900 | RATE_LIMITED |
Retryable. You went over your per-minute ceiling, or over the pre-authentication one. Carries Retry-After in whole seconds — back off for at least that long. Rate limiting explains how to tell the two apart. |
| 503 | 50300 | SERVICE_BUSY |
Retryable. We could not complete the request in time, the lookup of your key or of your allowance failed, or your account has no rate-limit allowance on record. The last of those is a misconfiguration on our side, not something you can fix — tell us, quoting the X-Request-Id. Your request was fine; retry it shortly. Deliberately distinct from 50000 so you can retry one and not the other. |
| 500 | 50000 | INTERNAL_ERROR |
A server-side error while reading the data that is not a timeout. Not caused by your request; retry later or contact support with the X-Request-Id if it persists. |
Some responses do not get this envelope. A path we do not serve — a known path with a trailing slash added (
/openapi/v1/assets/) is one — and a method the path does not accept, are both answered404astext/plain(Not Found) by the HTTP framework, before any endpoint code runs — an unroutable method is not distinguished from an unknown path, so we never return405. A client that assumes every response body is JSON will fail to parse these. Both still carryX-Request-Id.And in the rare case that a request fails so badly it is aborted mid-flight, the
500arrives with an empty body rather than the envelope.So a
404means one of two different things, and the body tells them apart. A JSON body with"code": 40400is/globalsaying the snapshot does not exist yet; atext/plainbody is the framework saying the path or method does not exist. Branch on the content type or on the presence ofcode, never on the status alone.