Publish Article API
Publish a draft article as the account behind your registered session, transitioning it from Draft to Published. WARNING: publishing posts a real, public announcement tweet on the account's timeline. This is not a preview or a private action, and it cannot be undone by re-editing the article: once published, the only way back is Delete Article, which unpublishes the article AND removes the announcement tweet. Requires a registered session. Billed at $0.0016 per call. Cost: $0.0016 per call.
POST article/publish moves a draft Twitter/X article from Draft to Published status. It posts a real, public announcement tweet linking the article to the account's timeline, an action that cannot be reversed by editing again. Requires a registered session and the article's id. Returns ok (boolean) and an article object containing tweet_id and public_url. Costs $0.0016 per call.
Authorization
bearerAuth Pass your API key as a bearer token on every request: Authorization: Bearer <API_KEY>.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/article/publish" \ -H "Content-Type: application/json" \ -d '{ "id": "QXJ0aWNsZUVudGl0eToyMDg2NjUwMjI4OTUwNjgzNjQ4" }'{ "ok": true, "article": { "id": "QXJ0aWNsZUVudGl0eToyMDg2NjUwMjI4OTUwNjgzNjQ4", "rest_id": "2086650228950683648", "title": "Why we rebuilt our onboarding from scratch", "preview_text": "Opening paragraph.", "lifecycle": "Published", "content_state": { "blocks": [ { "key": "a1b2c", "text": "Opening paragraph.", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [] } ], "entityMap": [] }, "cover_media": null, "media_entities": [], "author": { "id": "44196397", "username": "elonmusk", "name": "Elon Musk" }, "created_at_secs": 1786400000, "modified_at_secs": 1786400400, "first_published_at_secs": 1786400400, "visibility_setting": "Public", "tweet_id": "2086650300012345678", "public_url": "https://x.com/i/article/2086650228950683648" }}{ "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": "forbidden", "message": "The acting account is not authorized for this write action, or has no logged-in session."}{ "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 |
When to use
Use this endpoint when a draft article created via article/create is ready to go live and you want to transition it from Draft to Published, triggering the public announcement tweet. Reach for the sibling article/update_content or article/update_title endpoints instead if you still need to edit the draft before publishing, since publish is a one-way action.
Notes
- WARNING: this posts a real, public announcement tweet on the acting account's timeline. It is not reversible by editing the article again; the only reversal is Delete Article (which unpublishes and removes the announcement tweet) or Unpublish Article (which reverts to Draft but leaves the announcement tweet in place).
- [VERIFY] Request variable names and the audience/reply_control defaults are inferred from a captured X dialog default, not a captured request; not executed live end-to-end.
- Needs a session for the account you are publishing as: 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.
- caption is capped at 256 characters by X; longer captions are rejected upstream, not truncated by us.
Related endpoints
- Unpublish Article: Revert a published article back to Draft. The announcement tweet is left in place.
- Delete Article: Delete an article. A draft is hard-deleted; a published article is unpublished, then its announcement tweet is removed too.
- Get Article: Read an article via its announcement tweet, or your own via its entity id.
- Update Article Content: Replace an article's body with a new Draft.js content_state.
FAQ
Does publishing an article delete or replace the announcement tweet if I edit the article afterward?
No. Editing the published article's content does not update or remove the original announcement tweet. The endpoint description states publishing cannot be undone by re-editing, so the tweet stays live on the timeline exactly as it was posted even if the article content changes later.
How do I control who can see the article and who can reply to the announcement tweet?
Use the audience parameter to set who can read the published article and reply_control to set who can reply to the announcement tweet; both default to Everyone if omitted. These are independent settings, so you can restrict article readership without restricting replies, or vice versa.
Update Article Content POST
Replace the body of an article you own with a new content_state, identified by its entity id. content_state is Draft.js editor-state JSON ({ blocks: [...], entityMap: [...] }) that you build client-side; it is passed through to X verbatim and not validated on our side. Requires a registered session. Billed at $0.0016 per call. Cost: $0.0016 per call.
Unpublish Article POST
Revert a Published article back to Draft, identified by its entity id. The announcement tweet the original publish posted is left in place; use Delete Article to remove it too. X server-validates this transition and returns a 422 (invalid_lifecycle) if the article is not currently Published. Requires a registered session. Billed at $0.0016 per call. Cost: $0.0016 per call.