Errors
TwittrAPI status codes, error bodies, which errors are charged and refunded, and which ones to retry.
Errors come back as JSON with an error string:
{ "error": "Invalid tweet ID" }Some errors add fields: errorInfo (X's raw reply, for example on /username/to/id), errorCode, or a stable code string (for example on proxy errors). Do not parse the message text; it can change. Use the status code and, where present, code.
Status codes
"Charged" means the call costs one call's price. "Refunded" means it was charged and the amount was put back right away; the response then has the header X-Refunded: true. "Not charged" means the request was rejected before billing.
| Status | When | Billing |
|---|---|---|
| 200 | Success. | Charged |
| 400 | A required parameter is missing or malformed (for example no listId, or more than 500 IDs), or X answered with an error the API could not classify. | Refunded |
| 401 | Missing or unknown API key. | Not charged |
| 402 | No balance left, or the trial expired. | Not charged |
| 403 | Key deactivated, or the endpoint is not in your plan. | Not charged |
| 403 | X refused the request for this region. | Charged |
| 404 | X says the user, tweet or other object does not exist, or the path does not exist. | Charged |
| 407 | Account actions only: your proxy rejected the credentials, could not be reached, timed out or refused the connection to X. See Proxy errors. | Refunded |
| 408 | The request took longer than 60 seconds. | Refunded |
| 409 | The request failed with 400 on every automatic retry (see below). | Refunded |
| 422 | A parameter has an invalid value (non-numeric ID, bad username, unsupported search operator) or X rejected the input. | Charged |
| 429 | Rate limit for your key exceeded. See Rate limits. | Not charged |
| 500, 502, 503, 504 | Something failed on our side or at X. 502 also covers a search that got only empty pages from X after several retries. 504: X did not answer in time. | Refunded |
| X's status | Account actions pass X's own HTTP status through (for example 401 or 403 when X rejects the session). | Charged unless it is 400, 407, 408 or 5xx |
Endpoint pages list the validation errors of each endpoint under "Errors".
Proxy errors (407)
Account actions (/client/*, /premium/*) go out through the proxy you send. When that proxy fails, the request never reaches X and you get 407 with a code:
{ "error": "Your proxy rejected the credentials (407)", "code": "PROXY_AUTH_FAILED" }code | Meaning | What to do |
|---|---|---|
PROXY_AUTH_FAILED | The proxy refused the username or password (it answered 407, or SOCKS5 authentication failed). | Check the credentials in the proxy URL and that this IP is allowed by your proxy provider. |
PROXY_UNREACHABLE | No connection to the proxy (refused, unknown host). | Check host and port; is the proxy online? |
PROXY_TIMEOUT | Connecting through the proxy timed out (message Proxy connection timed out). | Retry, or use a faster proxy. |
PROXY_TUNNEL_FAILED | The proxy accepted the connection but would not open a tunnel to X (for example 502 or 403 from the proxy). | Check your proxy provider's target or country restrictions. |
PROXY_INVALID | The proxy value is not a valid proxy URL (422 instead of 407). | Use the form http://user:pass@host:port. |
Proxy errors are refunded at once, because nothing reached X, and they do not count as API errors on the status page. An invalid proxy URL (422) is charged like other input errors.
Automatic retries
A request that fails with 400 or a connection error is retried automatically. If every attempt fails, you get 409 (after 400s) or 502 / 503 (connection errors), all refunded. A response that needed a retry has the header X-Retried: true. You are billed once per request. Account actions (/client/*, /premium/*) are never retried, because the action may already have happened on X.
What to retry
| Status | Retry? |
|---|---|
| 408, 409, 429, 500, 502, 503, 504 | Yes, with exponential backoff (for example 0.5 s, 1 s, 2 s, up to a few tries). |
| 400 | Once at most. If it repeats, check the parameters. |
| 401, 402, 403, 404, 422 | No. Fix the key, balance or input first. |
| Account actions | Only after checking on X that the action did not happen. |
Temporary failures carry code and retryable in the body, and Retry-After when you can retry:
{"error": "Temporarily unavailable. You were not charged, please retry in a few seconds.", "code": "service_restarting", "retryable": true}| Code | Status | Meaning | What to do |
|---|---|---|---|
service_restarting | 503 | Briefly unavailable on our side. | Retry after Retry-After. |
x_unavailable | 503 | X is briefly not reachable. | Retry after Retry-After. |
upstream_timeout | 504 | No answer in time. | Retry. |
result_unknown | 502 | The connection broke during an account action. It may have gone through. | Check on X first. |
The official SDKs retry 429, 5xx and network errors with backoff by default, and raise typed errors (for example NotFoundError, RateLimitError, InsufficientBalanceError in Python).
from twittrapi import TwittrAPI, InsufficientBalanceError, NotFoundError, TwittrAPIError
client = TwittrAPI()
try:
user = client.users.get_by_username("someone")
except NotFoundError:
print("No such user")
except InsufficientBalanceError:
print("Top up your balance")
except TwittrAPIError as exc:
print(f"API error {exc}")Empty results are not errors
X often answers with 200 and an empty timeline instead of an error: for example for likes, which X made private. /user/by/username/{username} answers 200 with {"data": {}, "userExists": false} for a handle that does not exist.