Errors
Status codes, error codes and what to do.
Errors are JSON with a stable code, a human message and the request_id. No error is charged.
{ "error": { "code": "no_credits", "message": "balance is zero; buy a credit pack" }, "request_id": "..." }Match on code, never on message. The code is part of the contract and will
not change under you; the message is written for a person and may be reworded.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | bad_request | a parameter is missing or malformed | fix the request |
| 401 | missing_key, invalid_key | no key, or a revoked key | check the header |
| 402 | no_credits | balance is zero | buy a pack |
| 403 | access_denied | X does not serve this object to the reading session | try later, or a different object |
| 403 | product_unavailable | search product not available, for example Top | use Latest |
| 404 | not_found | no such user or tweet, or it is protected | nothing |
| 429 | rate_limited | over your requests-per-second limit | wait retry-after seconds |
| 503 | upstream_rate_limited, no_account, busy | X or our pool is saturated right now | retry after retry-after seconds, with backoff |
| 502 | upstream_error, query_id_stale, request_rejected | X changed something on its side | retry once; if it persists we are already paged |
| 500 | internal | our bug | send us the request_id |
RFC 9457 problem+json
Send Accept: application/problem+json and the same failure comes back in the
shape a generic HTTP client already understands. Nothing new is reported: type,
title, status and detail are the registered names for what error.code and
error.message already said, and the native error object is still there, so one
parser reads either shape.
{
"type": "https://xdataapi.io/docs/errors#no_credits",
"title": "Balance is zero",
"status": 402,
"detail": "balance is zero; buy a credit pack",
"code": "no_credits",
"error": { "code": "no_credits", "message": "balance is zero; buy a credit pack" },
"request_id": "..."
}Without that header you get the native shape, which is what both SDKs read. Neither is going away.
Retry policy we recommend
Retry 503 and 502 up to three times with 2, 4 and 8 seconds between attempts. Do not retry 4xx.
Because failed requests are free, retries never cost credits.
Read retry-after when it is present rather than guessing, and read RateLimit
on every response to throttle before a 429 happens at all. See
Authentication for the rate ladder.
Every code
missing_key
401. No key was sent. Put it in x-api-key, or send Authorization: Bearer xd_live_….
Free.
invalid_key
401. The key is unknown or has been revoked. Check it has not been deleted in the
dashboard, and that you are not sending a key from another environment. Free.
rate_limited
429. Over the account's requests-per-second limit, which is shared by every key
on the account. Wait retry-after seconds. The limit and what is left of it are on
every response in the RateLimit header, so a client that reads it need never hit
this. Free.
no_credits
402. The balance is zero. Buy a pack in the dashboard. There is no overage and no
invoice: the API stops rather than spending money you did not agree to. Free.
bad_request
400. Something about the request itself is wrong: a missing required parameter, a
malformed cursor, a batch list over 100, an unparseable body. The message names the
problem. Retrying unchanged will fail again. Free.
not_found
404. There is no such user or tweet, or it is protected, suspended or deleted. Not
a fault and not retryable. Free.
access_denied
403. X refused to serve this object to the session that asked (its error 37). Often
object-specific rather than account-wide, so another read may well succeed. Free.
product_unavailable
403. The search product asked for is not available, typically Top. Use Latest.
Free.
upstream_rate_limited
503. X is rate limiting our pool right now. Retry after retry-after seconds with
backoff. Free.
no_account
503. No healthy upstream account is available for this read at this moment. Transient;
retry with backoff. Free.
upstream_error
502. X answered with something we could not use. Retry once. If it persists, it is
already paging us. Free.
query_id_stale
502. X rotated the identifiers behind its own site and our client has not caught up
yet. This is the failure mode the whole repair loop exists for; it is measured on the
status page and fixed in minutes, not days. Retry with backoff. Free.
request_rejected
502. X rejected the shape of the upstream request. Same handling as upstream_error.
Free.
billing_disabled
503. Card checkout is not configured on this deployment. Nothing you can fix from the
client. Free.
internal
500. Our bug. Send the request_id to hello@xdataapi.io and we will find the exact
call in the logs. Free.