Reference

Error codes

An error always says what was wrong and, when that is possible, what should have been written instead. This page lists the cases, channel by channel, and separates what deserves a retry from what deserves none.

#The shape of an error

The detail is carried by the detail key. On validation errors, it names the offending value and, when a finite list exists, enumerates it.

400
{
  "detail": "Symbole 'PEPEUSDT' inconnu. Valeurs acceptees : BTCUSDT, ETHUSDT, ..."
}

Refusals tied to the plan have a fixed shape, set by the gateway: status, timestamp and detail, plus retry_after on time-based refusals (also present in the Retry-After header). The X-Deny-Reason header names the reason: rate, quota, family, websocket or key.

{
  "status": "error",
  "timestamp": 1775648290510,
  "detail": "Debit depasse : votre offre autorise un nombre limite de requetes par minute.",
  "retry_after": 1
}

The depth 403 is returned by the route itself, without X-Deny-Reason: its detail always starts with « Profondeur d’historique hors offre » and gives the maximum usable limit on that timeframe. The windows per plan are on the Usage limits page.

#HTTP codes

CodeCauseFix
400
Request refused
An unknown or disabled symbol, a depth beyond the field’s cap, an indicator parameter out of bounds.The response body names the offending value and, for a symbol, lists the ones that are accepted.
403
Refused by the plan or by the API key
Four reasons, named by the X-Deny-Reason header: API key missing, invalid or revoked (key); data family outside the plan (family); WebSocket stream outside the plan (websocket); or a history depth beyond the plan’s window, whose detail starts with « Profondeur d’historique hors offre ».On key, check that the header really is sent: some HTTP clients drop it after a redirect. On family and websocket, the data exists but is not in your plan. On depth, the detail gives the maximum limit to use.
422
Missing or malformed parameter
symbol missing, timeframe outside the list, multi-timeframe field requested without the @tf suffix, unknown field in ?fields=, invalid JSON body.On ?fields=, the response suggests the closest field. On a timeframe, it lists the accepted values.
429
Rate or quota exceeded
Either more requests per minute than your plan allows, or too dense a burst (X-Deny-Reason: rate, Retry-After: 1); or the account’s monthly quota is exhausted (X-Deny-Reason: quota, Retry-After: 3600).On rate, wait the second given by Retry-After. On quota, the counter restarts on the first day of the month (UTC); you can also change plan from the console.
503
Momentary unavailability
A read dependency is recovering, or the stream’s concurrent connection cap has been reached.Retry after the delay carried by Retry-After. The error is transient by construction.
500
Unexpected error
A fault on the service side, never caused by the request.Retry; if the response persists, report it with the timestamp carried by the response.

The most frequent confusions

SymptomReal cause
403 while the key is validRead X-Deny-Reason. family: the family of this endpoint is not in your plan. websocket: the stream is not in it. A detail « Profondeur d’historique hors offre »: the request goes back beyond the window of the plan. key with no apparent reason: an HTTP client that drops custom headers after a redirect; call the final URL.
429 while the pace is lowThe monthly quota of the account is exhausted (X-Deny-Reason: quota, Retry-After: 3600), not the rate limit. It restarts on the first day of the month, in UTC, or as soon as the plan changes.
422 with no clear message on a snapshotA multi-timeframe field asked for without its @tf suffix, or the opposite on a single-form field.
400 on a depth that looks reasonableEvery snapshot field has its own ceiling, in its own unit. The CVD stops at 200 windows, the inter-venue spreads at 50.
422 on a timeframe that is valid elsewhereThe long/short ratio refuses 1m, the intraday macro instruments refuse 30m, and the week only exists on the indicators.
An empty response that is not an errorA stablecoin on a futures metric, or options on an asset other than BTC and ETH. The unavailable key of the snapshot says which of the three reasons applies.
A parameter ignoredUnknown snapshot keys are silently ignored. A misspelt field is simply absent from the response.

#Indicators

Validation follows a fixed order (timeframe, number of results, symbol, parsing of the indicators, ceiling rule, reading of the candles), and the first failure wins. A corrected body can therefore reveal a second error.

CaseCode
Invalid timeframe, results below 1, unknown symbol400
Empty indicators list, or an entry that is not an object400
Missing id or type key, id malformed for this type400
Unsupported type400
Unknown parameter for this type (obv with a period, for example)400
Parameter of the wrong type, or below its minimum400
macd or adosc with fast greater than or equal to slow400
sar with acceleration greater than maximum400
Duplicate identifier400
Malformed body, more than 50 instances, field too long422
results plus warm-up exceeds 1000422
Insufficient data for this timeframe, stablecoins included422

#WebSocket stream

Every impossible request receives an op: error message that says what was expected: the list of valid symbols, that of the channels, the bounds of depth. Three situations produce not an error but a closure:

CodeReason
4003 · ceilingThe plan’s ceiling on simultaneous connections reached. The reason gives the ceiling. A disconnection frees its slot at once.
4003 · client too slowThe send queue has overflowed ten times. Consume faster, or reduce the number of channels. The reason distinguishes the two cases.
4001Explicit auth frame refused: the key it carries is invalid.
4002Session never authenticated within five seconds. A residual case, since the key is checked at the handshake.

The key is checked at the handshake, before the opening: a missing or invalid key, or a plan without the stream (Free), receives a 403 at the handshake, with X-Deny-Reason key or websocket. A 429 there signals too high an opening rate. Once connected, a channel whose family is not in the plan receives op: error (« canal … hors offre : la famille … n’est pas incluse dans votre abonnement ») and the whole subscription is refused. See Usage limits.

#MCP server

The errors follow a single pattern, Erreur (HTTP NNN) : message, so that an agent has only one case to recognise. Every tool call is judged as a REST request with the client’s key: a 403 for a family outside the plan or a 429 for quota come back as they are, with their detail. Two families are specific to it:

  • Unknown metric: the message lists the forty-six valid metrics.
  • Invalid targeting: two targets at once, missing target, wrong target, or a target passed to a whole-market metric. The error names the expected parameter, and it is raised before any network call.

A server fault gives rise to an automatic retry before being reported. A timeout returns Erreur (HTTP 504) after thirty seconds.

#Retry strategy

Not everything can be replayed. Replaying a faulty request produces exactly the same response, and consumes quota for nothing.

Python
def should_retry(code: int) -> bool:
    """What is transient, and what never will be."""
    # 429 and 503 will pass: the server says when.
    if code in (429, 503):
        return True
    # 500: a fault on the service side, a retry makes sense.
    if code == 500:
        return True
    # 400, 403, 422: the request is at fault. Replaying it as is will
    # produce exactly the same response.
    return False
  • Respect Retry-After rather than inventing a delay: the server knows when it will be ready.
  • Bound the number of attempts. Three or four are enough: beyond that, the problem is not transient.
  • Do not replay a 4xx, 429 aside. Correct the request.
  • On the stream, reconnect with an increasing delay, and treat a 4003 closure as a signal of overload on the client side: reconnecting without changing the consumption will reproduce the cut.