# 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](/mcp) connection.

Requires an API key, sent as `Authorization: Bearer <key>`.

## Request

```bash
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. |
