Errors And Limits
Every X error, what it means for what was sent, and each account's pace.
Early access. Tell us what you're building and we'll set you up. Contact us
Errors are problem JSON: { "type": "errors/…", "title", "status", "detail" },
with retry_at when waiting helps and the browser's own code when it came
from there: a post or reply the browser refused before pressing Post or Reply
is errors/not_sent with code naming why (replies_limited,
post_not_accepted, browser_busy…). A few answers use { "error", "message" } (a missing key, 402,
an unknown route). The rule to read every answer by: a 4xx means nothing
was done on X; a 5xx after the click means not sure: check before sending
again.
Pace#
| Limit | Applies to | Answer |
|---|---|---|
| 45 seconds between writes | posts, replies, Articles (not dry runs) | 429 errors/too_fast |
| 70 writes in 24 hours | posts, replies, Articles | 429 errors/daily_limit |
| 200 answers in 24 hours | replies sent with answer: true |
429 errors/answers_daily_limit |
| The account's browser is on another job | any call | 429 errors/browser_busy; for posts and replies, 422 errors/not_sent with code: "browser_busy" (nothing sent) |
Likes, reposts and their undo, follows, pins, profile edits and deletes aren't paced.
Any Route#
| HTTP | Type | Meaning |
|---|---|---|
401 |
unauthorized |
Missing or invalid API key. |
401 |
errors/disconnected_account, errors/reauth_required |
The account is disconnected, or X is signed out in its browser: sign it in again in the dashboard. |
402 |
subscription_required |
The workspace has no active trial or subscription. |
403 |
forbidden, errors/insufficient_permissions |
The key lacks accounts.read or accounts.write. |
403 |
errors/action_required, errors/account_restricted |
The account needs attention in the dashboard. |
404 |
errors/resource_not_found |
No such account in this workspace, or not an X account. |
409 |
errors/account_busy |
An X Article is being built on this account. |
422 |
errors/x_app_not_loaded |
X never loaded in the account's browser, even after a retry. Nothing was done. |
422 |
errors/proxy_auth_failed, errors/proxy_unreachable |
The account's proxy is down. Nothing was done. |
423 |
errors/x_account_locked |
X shows the account its lock page (x.com/account/access). Nothing was done until someone passes X's check. |
503 |
errors/service_unavailable, errors/no_client_session |
The account isn't ready, or its browser can't be reached. |
503 |
errors/maintenance |
Planned maintenance, with retry_at and Retry-After. |
504 |
errors/timeout |
X didn't answer in time. For a write: not sure, read the receipt. |
Lists On X#
X lists come in one page: a cursor on GET /posts/{post_id}/comments or
GET /users/{identifier}/posts answers 400 errors/invalid_parameters, and
every answer's cursor is null.
Writes#
| HTTP | Type | Meaning |
|---|---|---|
400 |
errors/missing_idempotency_key, errors/invalid_idempotency_key |
See Write Receipts. |
409 |
errors/in_progress |
The same key is still running or unsure. |
422 |
errors/idempotency_key_reused |
The same key was used for another request. |
422 |
errors/not_sent |
Posts and replies: the browser stopped before Post or Reply. code says why (post_not_accepted: X kept a long post in its box without Premium; replies_limited: the author limits who can reply; gif_not_found; browser_busy) and detail in words. Nothing was sent. |
422 |
errors/post_button_disabled, errors/reply_button_disabled |
X kept its button disabled. Nothing was sent. |
502 |
errors/*_unconfirmed |
Likes, reposts, follows, pins, profile edits: pressed, but X didn't show it kept. Check on X; repeating changes nothing. |
Profile Edits#
PATCH /users/me/edit checks every field before the browser opens, then
passes the browser's own answer through. Pictures and a handle can take a few
minutes: past 140 seconds the answer is 504 errors/timeout while the browser
goes on, and the receipt (GET /writes/{key}) gets its answer.
| HTTP | Type | Meaning |
|---|---|---|
400 |
errors/invalid_parameters |
A field X wouldn't take: name empty, on two lines or over 50 characters; bio over 160; location over 30 or on two lines; a website that isn't one https address (or is empty); a username that isn't 4–15 letters, digits or _; dry_run that isn't true or false; a picture that isn't a JPEG or PNG (a GIF or WebP is refused), is empty, isn't valid base64, or is over 2 MB (avatar) or 5 MB (banner). Nothing was changed. |
400 |
errors/invalid_* |
The browser's own check of one field, the same rules. Nothing was changed. |
403 |
errors/password_required |
X asks for the account's password to change the handle. Lempile never types it: the handle wasn't changed. |
409 |
errors/username_taken |
X says the handle isn't free (a handle is only free once the account that had it let it go). The handle wasn't changed. |
422 |
errors/username_field_not_found |
X's Change username page shows no Username field: X refuses a new handle while an X Premium check is under review. Nothing was changed. |
422 |
errors/username_refused, errors/username_check_failed, errors/username_save_unavailable, errors/username_not_kept |
X refused the handle, never said if it was free, offered no Save, or kept the old one. |
422 |
errors/profile_photo_input_not_found, errors/profile_photo_not_taken |
X's file picker or crop window for a picture didn't show. Nothing was saved. |
422 |
errors/profile_field_not_found, errors/profile_fields_changed, errors/profile_save_unavailable, errors/typing_failed |
X's Edit profile wasn't as expected. Nothing was saved. |
422 |
errors/profile_value_not_kept |
Saved, but X keeps another value. detail names the field and quotes any sheet X showed (X said: …). |
501 |
errors/feature_not_implemented |
A field an X profile doesn't have (headline, experience, picture_settings…), another type than X, or a LinkedIn account. Nothing was changed. |
502 |
errors/profile_unconfirmed, errors/username_unconfirmed |
Saved, but not read back: not sure. Read the profile (GET /users/me) before trying again. |
An answer that failed on the handle after the Edit profile part was saved
carries done, what X keeps from that part. Sending the same edit again types
nothing that's already as asked; pictures are given again.
Page Capture#
POST /accounts/{account_id}/capture answers 400 errors/invalid_parameters
for an address it doesn't open (it lists them; an X post is exactly
https://x.com/<handle>/status/<digits>, no query) and for a clip.selector
on an X post. When the page gave no picture the answer is still 200, with
ok: false and the browser's code; nothing was clicked or typed. See
Page Capture.
code |
Meaning |
|---|---|
x_post_author_mismatch |
X shows the post under another handle than the address's, or no author it could read. |
x_post_protected |
The post is from a protected account. |
x_post_deleted |
X says the post doesn't exist: deleted, or never posted. |
x_post_age_restricted |
X shows the post only behind an age or sensitive-content warning, never clicked through. |
x_post_unavailable |
X shows its "This post is unavailable" notice. |
x_post_not_loaded |
X ran but didn't show the post within 25 seconds. Try again later. |
x_signed_out, x_app_not_loaded |
X is signed out in the account's browser, or didn't load. |
left_allowed_hosts, url_not_allowed |
The page went to another address after loading, or the browser's own check refused the address. |
capture_blank |
The picture came out blank, or X drew the post's box empty: it isn't returned. |
counts_not_hidden |
Asked without counts (x_post: { counts: false }), but the views row and action bar couldn't be hidden: no picture. |
element_not_found, browser_busy, no_picture, capture_failed |
No element for the selector, a busy browser (try again in a minute), or no picture. |
The route pages list each route's own errors, such as errors/not_own_post,
errors/delete_unavailable, errors/follow_limited or
errors/analytics_unavailable.
Was this page helpful?