Read an X Article
GET /v1/x/posts/{id}/article
Returns the X Article a post carries: title, cover image, and the full body as plain text, Markdown, and X's own blocks (headings, lists, quotes, code, links, images, and embedded posts), with every image and video in it. Pass the id of the post that shares the Article — the number in its x.com/…/status/ link; an x.com/i/article/ link carries the Article's own id, which is not a post id. A post that carries an Article has only a link as its text, so use this, or article on the post, to read it. 404 when the post does not exist or is not an Article.
Price: 1 credit per call, plus 1 credit per X post returned. Failed and refused calls cost nothing.
Rate limit: Counts as 1 request against your per-minute rate limit.
MCP tool: get_x_article, on every MCP connection.
Requires an API key, sent as Authorization: Bearer <key>.
Request
curl --request GET \
--url https://api.weirapp.io/v1/x/posts/ID/article \
--header "Authorization: Bearer $WEIR_API_KEY"Path parameters
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | Numeric post id: the digits after /status/ in a post's URL. · matches ^\d{1,19}$ |
Response
The Article, with the post and author that carry it.
200
| Field | Type | Required | Notes |
|---|---|---|---|
data |
object | yes | |
data.id |
string | yes | The Article's own id, distinct from the id of the post carrying it. |
data.url |
string | yes | |
data.title |
string | yes | |
data.previewText |
string or null | yes | The opening lines X shows in a feed. |
data.coverImage |
object or null | yes | |
data.coverImage.id |
string | yes | X's media id; blocks refer to media by this id. |
data.coverImage.type |
string | yes | X's own media type names. · photo, video, animated_gif |
data.coverImage.url |
string or null | yes | The original image for a photo; the highest-bitrate MP4 for a video or GIF. Null when X offers only a stream. |
data.coverImage.width |
integer or null | yes | |
data.coverImage.height |
integer or null | yes | |
data.publishedAt |
string or null | yes | |
data.updatedAt |
string or null | yes | |
data.text |
string or null | yes | The full body as plain text. Null in lists of posts, where X sends only the preview: get the post, or its article, for the body. |
data.markdown |
string or null | yes | The full body as Markdown — headings, emphasis, lists, quotes, code, links, images, and embedded posts — for reading or for a model. Null wherever text is. |
data.blocks |
array of object or null | yes | The body in X's own block structure. Null wherever text is. |
data.blocks[].type |
string | yes | X's block type: unstyled (paragraph), header-one, header-two, header-three, unordered-list-item, ordered-list-item, blockquote, code-block, or atomic (an embed: see entities). |
data.blocks[].text |
string | yes | |
data.blocks[].depth |
integer | yes | Nesting level for list items; 0 otherwise. · min 0 |
data.blocks[].styles |
array of object | yes | Inline styles over spans of text. |
data.blocks[].styles[].offset |
integer | yes | Start, in UTF-16 code units of text. · min 0 |
data.blocks[].styles[].length |
integer | yes | min 0 |
data.blocks[].styles[].style |
string | yes | X's inline style name: Bold, Italic, Strikethrough, … |
data.blocks[].entities |
array of object | yes | Links and embeds attached to spans of text. |
data.blocks[].entities[].offset |
integer | yes | Start, in UTF-16 code units of text. · min 0 |
data.blocks[].entities[].length |
integer | yes | min 0 |
data.blocks[].entities[].type |
string | yes | X's entity type: LINK, MEDIA, TWEET (an embedded post), DIVIDER, MARKDOWN (a code or formatted snippet), or another X adds later. |
data.blocks[].entities[].url |
string or null | yes | The target of a LINK. |
data.blocks[].entities[].mediaIds |
array of string | yes | For MEDIA: ids into the Article's media list, in display order. |
data.blocks[].entities[].postId |
string or null | yes | For TWEET: the embedded post's id. |
data.blocks[].entities[].markdown |
string or null | yes | For MARKDOWN: the snippet's source. |
data.media |
array of object | yes | Images and videos in the body, in order. Empty in lists of posts. |
data.media[].id |
string | yes | X's media id; blocks refer to media by this id. |
data.media[].type |
string | yes | X's own media type names. · photo, video, animated_gif |
data.media[].url |
string or null | yes | The original image for a photo; the highest-bitrate MP4 for a video or GIF. Null when X offers only a stream. |
data.media[].width |
integer or null | yes | |
data.media[].height |
integer or null | yes | |
data.postId |
string | yes | The post that carries the Article. |
data.postUrl |
string | yes | |
data.author |
object | yes | |
data.author.id |
string | yes | |
data.author.username |
string | yes | |
data.author.name |
string | yes | |
data.author.verified |
boolean | yes | |
data.author.followersCount |
integer | yes | min 0 |
data.author.profileImageUrl |
string or null | yes | |
meta |
object | yes | |
meta.requestId |
string | yes | |
meta.nextCursor |
string or null | no |
Errors
| Status | Meaning | What to do |
|---|---|---|
400 |
The request failed validation. | The request was malformed. The details object names the field. |
401 |
The API key is missing, malformed, expired, or revoked. | The key is missing, malformed, revoked, or expired. |
402 |
The account is out of credit. Not retryable: this cannot clear until the billing period resets or the plan changes. | Out of credits. Never retry. Only a person can clear it, by adding credits at https://weirapp.io/dashboard/billing. Retrying never will. |
403 |
The API key does not carry the scope this route requires. | The key is valid but lacks the scope this operation needs. |
404 |
The requested resource does not exist. | No such resource, or it is not public. |
429 |
The per-minute rate limit is exhausted. Retryable: wait for Retry-After and try again. | Rate limited. Retryable: wait for Retry-After, then try again. |
500 |
An unexpected error occurred. | Something failed on our side. Retryable with backoff. |
503 |
The source is temporarily unavailable. The request is safe to retry. | A source was unreachable. Retryable with backoff. |