List Draft Tweets API
List every draft on the account behind your registered session. Each row carries the draft id, its text, and thread_truncated, which is true when the draft is a thread and the text you get is only its first tweet. Ordering is X's: pass ascending=true to ask for the oldest first, omit it for X's default. Billed at $0.0016 per call. Cost: $0.0016 per call.
GET draft/list returns every draft saved on the account behind your registered twitterapis session. Each row carries draft_tweet_id, text, and thread_truncated for a thread whose text is only its first tweet. Pass ascending=true to ask X for the oldest first. One call returns the list for $0.0016.
Authorization
bearerAuth Pass your API key as a bearer token on every request: Authorization: Bearer <API_KEY>.
In: header
Query Parameters
Pass true to ask X for the oldest draft first. Anything else, including omitting it, sends ascending=false, which is what X's own composer sends. The resulting order is X's and is not re-sorted here.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/draft/list?ascending=false"{ "drafts": [ { "draft_tweet_id": "2086650228950683648", "text": "Draft: our Q1 changelog is nearly done.", "thread_truncated": false } ], "count": 1}{ "error": "bad_request", "message": "Missing or malformed parameter. Fix the request before retrying."}{ "error": "unauthorized", "message": "The API key is missing, malformed, or revoked. Check the Authorization header."}{ "error": "insufficient_credits", "message": "Your balance is exhausted. Top up your credits to continue."}{ "error": "not_found", "message": "The resource does not exist, for example a deleted tweet or a private account."}{ "error": "rate_limited", "message": "Too many requests. Back off and retry with exponential backoff."}{ "error": "server_error", "message": "Something failed on our side. Retry with backoff; if it persists, contact support."}Pricing
| Unit | Price |
|---|---|
| Per call | $0.0016 |
| Per 1,000 calls | $1.60 |
One call is the whole request, with no cursor to follow, so the per-1,000-records row below is indicative: the number of calls you make depends on how often you poll, not on how many drafts you hold.
When to use
Use draft/list to find the draft_tweet_id you need for an edit or a delete, or to show a human what is waiting for approval. It reads only your own account: there is no way to read another account's drafts, because drafts are private to the account that holds them.
Notes
- There is no cursor to pass. The only variable X's own composer sends on this operation is ascending, and the response we read carries no cursor of any kind, so one call is the whole request. Whether X itself caps the number of rows it will return has not been observed.
- There is no created_at. X sends no timestamp on a draft row, so none is invented here; ordering is what ascending controls.
- NOT BILLED, narrowly: a response that is BOTH partial AND carries no row with usable text costs nothing, even though it answers 200. An honest empty list is NOT that case: it is a correct answer, and it is billed like any other read.
- Needs a session for the account you are reading: either register one once (POST /customer/session), or pass per-call inline credentials as x-auth-token and x-ct0 request headers, which let a single API key act as many accounts. Returns 409 session_required if neither is supplied, 401 session_dead when the session has expired.
Related endpoints
- Create Draft Tweet: Save a private draft on your own account. Nothing is posted.
- Edit Draft Tweet: Replace the contents of an existing draft on your own account.
- Delete Draft Tweet: Delete a draft from your own account by ID.
FAQ
What does partial: true mean here?
It means we read X's answer but did not fully understand all of it: some rows were dropped, or a row's text could not be parsed. It is deliberately different from an empty list. An empty drafts array with no partial flag means the account genuinely has no drafts.
Why is text only the first tweet of my thread?
Because the draft is a thread and this endpoint returns its head. thread_truncated is true on exactly those rows, so you can tell a truncated thread from a complete single tweet rather than silently showing a partial post as whole.
Can I list another account's drafts?
No. Drafts are private to the account that holds them, so this reads only the account behind the session you registered or the inline credentials you passed.
Delete Draft Tweet POST
Delete a draft owned by the account behind your registered session, identified by its id. The draft is removed from X's composer. Nothing was ever public, so this publishes nothing and retracts nothing. Billed at $0.0008 per call, the same as a standard read. Cost: $0.0008 per call.
Schedule Tweet POST
Schedule a tweet from the account behind your registered session for a future instant. Unlike a draft, this WILL post publicly at execute_at unless you delete it first. execute_at is epoch SECONDS and must be in the future; a millisecond value is refused by name rather than scheduled tens of thousands of years out. Billed at $0.0016 per call. Cost: $0.0016 per call.