# How to Publish an X Article with an API Canonical: https://www.twitterapis.com/answers/how-to-publish-an-x-article-api Description: Create a draft, set its title, content and cover, then publish and X posts the announcement tweet. Six premium article writes at $0.0016 per call. Generated: 2026-08-26T06:52:18.684Z ---[Pricing](/pricing)[Docs](https://docs.twitterapis.com)[Blog](/blogs) Compare and Tools [MCP Server](/mcp)[Integrations](/integrations)[Language Clients](/sdk)[Free Tools](/tools)[Twitter ID Finder](/tools/twitter-id-finder)[Twitter API Cost Calculator](/twitter-api-cost-calculator)[Twitter Search API](/twitter-search-api)[Twitter Followers API](/twitter-followers-api)[Twitter Scraper](/twitter-scraper)[Twitter API Use Cases](/twitter-api-usecases)[Twitter API Rate Limits](/twitter-api-rate-limits)[Twitter Unofficial API](/twitter-unofficial-api)[Twitter Free API](/twitter-free-api)[Twitter API Alternatives](/twitter-api-alternatives)[TwitterAPIs vs Tweepy](/twitterapis-vs-tweepy)[TwitterAPIs vs RapidAPI](/twitterapis-vs-rapidapi)[TwitterAPIs vs GetXAPI](/twitterapis-vs-getxapi) Company [About](/about)[Status](/status)[Affiliates](/affiliates)[Trust](/privacy-and-data-handling)[Changelog](/changelog)[Contact](/contact) [Start Free](/signup) 1. [Home](/) 2. /[Answers](/answers) 3. /How do you publish an X Article with an API? # How do you publish an X Article with an API? Last updated August 24, 2026 An Article starts empty and gets filled in afterwards. article/create returns a draft carrying an entity id, update\_title, update\_content and update\_cover\_media edit that draft, and article/publish flips its lifecycle to Published while posting the announcement tweet that links to it. Six of the nine routes are premium writes at $0.0016; get, list and delete are $0.0008. Every rate here is the pricing TwitterAPIs publishes. The billed rate is $0.0008 per call; $0.04 per 1,000 tweets is derived from it at a full 20-tweet page, which is the default page size rather than a guaranteed yield (source: [twitterapis.com/pricing](/pricing)). ## The order of calls, start to finish There is no single save call, so publishing is a sequence rather than one request with a large body. It runs article/create to open an empty draft, then article/update\_title, then article/update\_content, then article/update\_cover\_media if you want a cover, then article/publish. Five calls for a first publish, and only the last of them is visible to anybody else. Every route in that sequence needs a registered session for the acting account, because a draft lives on your account and cannot be created or edited from a shared pool. The three routes outside the sequence are article/get, article/list and article/delete, which is what you use afterwards to find and remove what you made. ## What the create call hands back article/create takes no parameters at all. It returns ok and an article object whose title is null, whose preview\_text is an empty string, whose content\_state holds empty blocks and entityMap arrays, and whose lifecycle reads Draft. Two identifiers come back and they are not interchangeable. id is the entity id, a base64 looking string, and it is what every later call in this family expects. rest\_id is the numeric id that ends up inside the public URL after publishing. Alongside them sit author, created\_at\_secs and modified\_at\_secs as second precision epochs, plus first\_published\_at\_secs, visibility\_setting, tweet\_id, cover\_media and public\_url, all null on a fresh draft and each filled in by a later step. ## Setting the title article/update\_title takes the entity id and a title string, and either may travel in the query string or in a JSON request body, whichever suits your client. It works on a draft and on an already published article, so correcting a headline after the fact does not mean unpublishing first. The response is the whole article object again, in the same shape create returned, with title now set and modified\_at\_secs moved forward. That consistent envelope is why a client can treat every article write identically: send a change, read the current state of the article back, and never keep a local copy that can silently drift from what X is actually holding. ## Replacing the body with Draft.js JSON article/update\_content replaces the body. content\_state is Draft.js JSON, an object carrying a blocks array and an entityMap, and this is the one route where the payload must go in the JSON request body rather than the query string. A missing or non object value is a 400. Each block carries key, text, type, depth, inlineStyleRanges and entityRanges. The structure is passed through verbatim and is not validated on the way, which means your client owns it end to end: nothing here repairs a malformed block or rejects one on your behalf. preview\_text on the response is derived from what you sent, so reading it back is a cheap check that the text landed where you thought it would. ## Attaching a cover image article/update\_cover\_media attaches, it never uploads. Send the image to media/upload first, keep the media\_id it returns, then call this route with the article id and that media\_id. An optional media\_category defaults to DraftTweetImage, which is exactly what X's own article editor sends, so leaving it alone is usually correct. The response fills cover\_media with media\_id, media\_key, original\_img\_url, original\_img\_width and original\_img\_height, so the dimensions X recorded come back to you rather than having to be measured locally before or after. cover\_media stays null until this call lands, so an article published without it simply has no cover rather than falling back to something generated. Like the title route it works on a draft or a published article, which makes swapping the cover on something already live a single call. ## Publishing, and the announcement tweet article/publish is the only route here that anybody else can see. It moves lifecycle to Published, sets visibility\_setting to Public, fills tweet\_id with the id of the announcement tweet it has just posted, sets first\_published\_at\_secs, and returns public\_url in the form x.com/i/article/ followed by the rest\_id. Three optional parameters shape it. audience controls who can see the article and reply\_control controls who can reply to the announcement, both defaulting to Everyone, the most permissive setting, when left out. caption adds up to 256 characters of text to the announcement post. first\_published\_at\_secs is stamped on this call and survives a later unpublish, so it stays the record of when the piece first went out. Because a real tweet goes out, treat a call here as a shipping action rather than another save. ## Unpublish leaves the announcement standing article/unpublish walks lifecycle back to Draft and clears visibility\_setting and public\_url. It fails when the article is not currently Published, because X validates that transition upstream rather than quietly accepting a no op. What it does not do is remove the announcement tweet, which stays live in the timeline with its id still sitting on the article object as tweet\_id. So an article you have quietly unpublished still has a public post pointing at it, and anyone following that link learns the article is gone instead of never having seen the link at all. The response shows exactly that asymmetry: tweet\_id is still populated while public\_url has gone null. If you want both retracted, unpublish is the wrong route. ## Delete, and its two fast path hints article/delete handles both shapes. A draft is hard deleted directly. A published article is unpublished first and then its announcement tweet is removed, which is what makes it the complete retraction. Two optional parameters are purely about speed: pass lifecycle and tweet\_id from an earlier create or list response and the route skips a lookup, whereas omitting them costs an internal scan of your Draft list and then your Published list, the first 100 of each. The failure mode is handled honestly rather than optimistically. If the announcement tweet id cannot be resolved, the article is left as a Draft instead of reporting a success that did not happen. It answers with ok, deleted and an echo of the id. ## Reading articles back, and the price split Two routes read. article/list pages your own articles filtered by lifecycle, draft or published, defaulting to draft, with count clamped to 1 through 100 and a cursor for paging. X exposes no combined view, so seeing everything means calling it twice, and an empty array is a valid answer rather than a failure. article/get has two mutually exclusive forms: id or url, meaning the announcement tweet, is a public pooled read that needs no session and works on published articles only, while article\_id is an owner read that reaches your drafts too. On price, the six writes from create through unpublish sit in the premium tier at $0.0016, while get, list and delete are charged at the ordinary read rate. That rate is $0.0016 a call, per our own rate card. ## The nine article routes and what they bill Route Does what Price per call article/create Opens an empty draft and returns its entity id $0.0016 article/update\_title Sets or corrects the headline $0.0016 article/update\_content Replaces the body with Draft.js JSON $0.0016 article/update\_cover\_media Attaches an uploaded media\_id as the cover $0.0016 article/publish Publishes and posts the announcement tweet $0.0016 article/unpublish Returns it to Draft, announcement stays live $0.0016 article/get and article/list Read one article, or page your own by lifecycle $0.0008 article/delete Removes the article and its announcement tweet $0.0008 > Each media object may have multiple display or playback variants, with different resolutions or formats. X Developer Platform, media upload documentation. [Source](https://docs.x.com/x-api/media/introduction) ## Questions and answers What is an X Article? It is X's long form post format, stored as its own entity rather than as a tweet. It carries a title, a cover image, a Draft.js content body, an author and a lifecycle that is either Draft or Published. When published it gets a public\_url of the form x.com/i/article/ plus a real announcement tweet, whose id comes back on the object as tweet\_id. What is the difference between id and rest\_id? id is the entity id, a base64 looking string, and it is the value every write in this family expects: title, content, cover, publish, unpublish and delete all take it. rest\_id is the numeric id that appears inside the public article URL after publishing. They arrive together on the create response and are not interchangeable, so store both rather than picking one. What format does the article body take? Draft.js JSON, an object with a blocks array and an entityMap. Each block carries key, text, type, depth, inlineStyleRanges and entityRanges. It must be sent in the request body rather than the query string, and it is forwarded to X untouched with no validation on the way through, so your client builds and edits that structure itself. A missing or non object value is a 400. How do I set a cover image? Upload the image through media/upload first, keep the media\_id it hands back, then call update\_cover\_media with the article id and that media\_id. The cover route attaches, it never uploads. media\_category defaults to DraftTweetImage, matching what X's own editor sends. The reply returns cover\_media with the media\_key and the original image width and height filled in. Until that call lands, cover\_media is null on the article object, so publishing early means publishing without a cover. Can I edit an article after it is published? The title and cover routes are both documented as working on a draft or an already published article, so fixing a headline or swapping a cover image is a single call with no unpublish step in front of it. Every write returns the full article object back, so you can read the new state immediately rather than assuming the change landed and caching your own copy. Can I take a published article back down? Partly. article/unpublish returns the lifecycle to Draft and clears public\_url, but it leaves the announcement tweet standing, so the post that told everyone about it is still in the timeline. Use article/delete if you want both gone, since it unpublishes first and then removes the announcement. Passing lifecycle and tweet\_id as hints skips an internal list scan. After an unpublish the object keeps tweet\_id populated while public\_url goes null, which is the shape to check for. How do I find an article I created earlier? Call article/list, which pages your own articles by lifecycle and defaults to draft. X exposes no combined view, so run it once for draft and once for published to see everything. Take the entity id from a row and pass it to article/get as article\_id for the owner read, which is the only form that reaches drafts, since a draft has no announcement tweet. Do I need a logged in account for this? Every article write does, plus the list route and the owner form of get, because drafts live on your account and cannot come from a shared pool. Register auth\_token and ct0 through the session route, or send them per call as headers. Only the public form of article/get, keyed on the announcement tweet id or its status URL, works with no session at all. Why does article/get return a 404? On the public form, a 404 covers three separate cases: the tweet does not exist, it is protected, or it exists but is not an article announcement. On the owner form it additionally covers an article that is not owned by the calling account. The status does not distinguish between them, so treat it as not readable by you rather than as proof that nothing is there. What does one full publish cycle cost? Create, set the title, set the content, attach a cover, publish: five premium writes at $0.0016 each is eight tenths of a cent. Reading the result back with get, or listing your drafts, is charged at the standard read rate instead, as is delete. Nothing recurs, so an article that is never edited again is billed exactly once. The $0.50 of credit a new account starts with covers roughly 62 complete publish cycles before you pay anything. ## Keep reading - [Twitter REST API](/twitter-rest-api?utm_source=aio&utm_medium=organic&utm_campaign=aeo-answers-how-to-publish-an-x-article-api) - [Twitter API pricing](/twitter-api-pricing?utm_source=aio&utm_medium=organic&utm_campaign=aeo-answers-how-to-publish-an-x-article-api) - [How do you post a tweet with an image?](/answers/how-to-post-a-tweet-with-an-image-api?utm_source=aio&utm_medium=organic&utm_campaign=aeo-answers-how-to-publish-an-x-article-api) - [Quickstart](/quickstart?utm_source=aio&utm_medium=organic&utm_campaign=aeo-answers-how-to-publish-an-x-article-api) - [Full API reference](https://docs.twitterapis.com/docs) - [Threads as the shorter-form alternative](/answers/how-to-get-a-full-twitter-thread-api?utm_source=aio&utm_medium=organic&utm_campaign=aeo-answers-how-to-publish-an-x-article-api) ### Start with $0.50 in free credits No credit card. Roughly 12,500 tweets to test every endpoint. [Get your API key](/signup?utm_source=aio&utm_medium=organic&utm_campaign=aeo-answers-how-to-publish-an-x-article-api)[See pricing](/twitter-rest-api?utm_source=aio&utm_medium=organic&utm_campaign=aeo-answers-how-to-publish-an-x-article-api) [ TwitterAPIs](/) The cheapest pay-as-you-go Twitter and X API. $0.0008 per call, which works out to $0.04 per 1,000 tweets on a full 20-tweet page. No subscriptions and no developer account. ## Product / API - [Pricing](/pricing) - [Pay-Per-Use Pricing](/pay-per-use-pricing) - [Cost Calculator](/twitter-api-cost-calculator) - [Rate Limits](/twitter-api-rate-limits) - [MCP Server](/mcp) - [Integrations](/integrations) - [Language Clients](/sdk) - [Changelog](/changelog) - [Status](/status) ## Developers - [Documentation](https://docs.twitterapis.com) - [API Reference](https://docs.twitterapis.com/docs/reference/search/tweet-advanced-search) - [User Info](https://docs.twitterapis.com/docs/reference/user-reads/user-info) - [User Tweets](https://docs.twitterapis.com/docs/reference/user-reads/user-tweets) - [Advanced Search](https://docs.twitterapis.com/docs/reference/search/tweet-advanced-search) - [Verified Followers](https://docs.twitterapis.com/docs/reference/follower-graph/user-verified-followers) ## Resources / Compare - [Answers](/answers) - [Reviews](/reviews) - [Free Tools](/tools) - [Twitter ID Finder](/tools/twitter-id-finder) - [Get a Twitter API Key](/twitter-api-key) - [Official X API Comparison](/twitter-api-pricing) - [Twitter API Use Cases](/twitter-api-usecases) - [Twitter API Alternatives](/twitter-api-alternatives) - [Twitter Unofficial API](/twitter-unofficial-api) - [Twitter Free API](/twitter-free-api) - [TwitterAPIs vs twitterapi.io](/twitterapis-vs-twitterapi-io) - [TwitterAPIs vs GetXAPI](/twitterapis-vs-getxapi) - [TwitterAPIs vs TweetAPI](/twitterapis-vs-tweetapi) - [TwitterAPIs vs TwexAPI](/twitterapis-vs-twexapi) - [TwitterAPIs vs RapidAPI](/twitterapis-vs-rapidapi) ## Legal - [About](/about) - [Security](/security) - [Trust](/privacy-and-data-handling) - [Terms of Service](/terms-of-service) - [Affiliates](/affiliates) - [Contact](/contact) - [Jobs](/jobs) © 2026 TwitterAPIs. All rights reserved. TwitterAPIs is an independent third-party API for developers and researchers. Not affiliated with, endorsed by, or sponsored by X Corp. All systems operational