How to Read X Articles via API (Full Text, Markdown and Images)
An X Articles API guide: why a post that shares a Twitter Article has only a t.co link as its text, and how to get the title, full body, Markdown and images.
By Tarun Khatri · · 8 min read
X Articles are long-form posts written on X. When someone publishes one, X creates an ordinary post that shares it, and that post's text is usually nothing but a t.co link. To read the Article through an API, you ask for its Article data specifically. On the official X API that's the article post field. On Weir it's GET /v1/x/posts/{id}/article, which returns the title, cover image, and full body as plain text, Markdown, and X's own blocks, plus every image and video in it. This guide shows both, with curl, Python, and the MCP tool get_x_article.
Disclosure: Weir is our product. The official X API route is covered too, with its pricing.
What X Articles are
According to X's Help Center, "Articles is a new way to share long form written content on X." As of October 2026, publishing them is limited to paying subscribers: X Premium and Premium+, the plans on The X Subscription, Premium Businesses, and Premium Organizations. Once an Article is published, anyone can read it, subject to the audience the author picked. Authors can also limit an Article to their subscribers.
An Article is richer than a post. The same help page says it can include "images, video, GIFs, posts, and links", with "headings, subheadings, bold, italics, strikethrough, indentation, numerical and bulleted lists." Published Articles live on the author's profile under an Articles tab. Since June 2026, X has also offered endpoints to draft and publish Articles through its API.
Why the post's text is only a t.co link
When an Article is published, X creates a post that carries it. That's the post you see in timelines and the one with an x.com/<user>/status/<id> URL. Its text is just a short link to the Article, so a normal post lookup gives you a URL and nothing to read.
We saw this ourselves on a public Article, "Tokenized US Stocks Have a Path Onshore" by @cryptomoza92. Fetching the post returns:
{
"id": "2108278999638274221",
"text": "https://t.co/5g4gzdUFg1",
"lang": "zxx",
"entities": {
"hashtags": [],
"mentions": [],
"urls": ["https://x.com/i/article/2108274843242364928"]
}
}
lang is zxx, the code for "no linguistic content", because the text really is just a link. Developers report the same thing on X's developer forum: with the default payload, the text "is basically the t.co link for an Article".
Note the two different ids. 2108278999638274221 is the post id. 2108274843242364928, in the x.com/i/article/... link, is the Article's own id. API lookups take the post id.
Option 1: the official X API
The official X API v2 does expose Articles. X's data dictionary lists an article post field, "Contains metadata for the Article present in this Tweet", with the note "Use this to get the text and entities of an Article." You request it with tweet.fields=article on a post lookup.
Two caveats as of October 2026:
- Cost. Reads are billed under X's pay-per-usage pricing: $0.005 per post read, so one Article costs about half a cent. You need a developer account and prepaid credits.
- Shape. The published OpenAPI spec types
articleas a bare object, with no documented sub-fields. Check the actual response before you build a parser on it.
If you already have X API access, this is the most direct route, and X is the source of truth. X API pricing in 2026 covers the full rate card.
Option 2: Weir's X Articles API
Weir has two ways in. Both take the post id, or you can pass a full x.com/.../status/... URL to the MCP tool.
| Endpoint | Returns | Cost |
|---|---|---|
GET /v1/x/posts/{id} |
The post, with an article field when it carries one |
1 credit per call + 1 per post |
GET /v1/x/posts/{id}/article |
Only the Article, plus the post id, post URL, and author | 1 credit per call + 1 per post |
Either way, one Article costs 2 credits. A post that isn't an Article returns null for article on the first endpoint and 404 on the second. Failed calls cost nothing.
The Article object
These fields are from the getXArticle response schema in Weir's OpenAPI contract:
| Field | What it holds |
|---|---|
id, url |
The Article's own id and its x.com/i/article/... URL |
title |
The headline |
previewText |
The opening lines X shows in a feed |
coverImage |
{ id, type, url, width, height }, or null |
publishedAt, updatedAt |
ISO timestamps |
text |
The full body as plain text |
markdown |
The full body as Markdown: headings, emphasis, lists, quotes, code, links, images, embedded posts |
blocks |
X's own block structure: type (unstyled, header-two, unordered-list-item, atomic, ...), text, depth, styles, entities |
media |
Every image and video in the body, in order: { id, type, url, width, height } |
postId, postUrl, author |
The post that carries the Article and who wrote it (only on /article) |
markdown is what most people want, whether for reading, storing, or passing to a language model. blocks is for rebuilding the exact layout. Each block's entities point to links (LINK), images (MEDIA, with mediaIds into media), and embedded posts (TWEET, with a postId).
One gotcha: in lists of posts, such as search results and timelines, text, markdown, and blocks are null and media is empty, because X only sends the preview there. Use the list to find Articles, then fetch each one for its body.
curl
curl --url https://api.weirapp.io/v1/x/posts/2108278999638274221/article \
--header "Authorization: Bearer $WEIR_API_KEY"
Trimmed response:
{
"data": {
"id": "2108274843242364928",
"url": "https://x.com/i/article/2108274843242364928",
"title": "Tokenized US Stocks Have a Path Onshore",
"previewText": "On September 17 the SEC issued a five-year order that, for the first time, lets a tokenized share of a US-listed company trade onchain in the United States...",
"coverImage": {
"id": "2108278678669135872",
"type": "photo",
"url": "https://pbs.twimg.com/media/HUIcScGakAAAFQQ.png",
"width": 1500,
"height": 600
},
"publishedAt": "2026-10-08T19:30:58.000Z",
"text": "...",
"markdown": "...",
"blocks": [{ "type": "unstyled", "text": "...", "depth": 0, "styles": [], "entities": [] }],
"media": [
{
"id": "2108278928507076608",
"type": "photo",
"url": "https://pbs.twimg.com/media/HUIcg-0bAAAiRBa.png",
"width": 1600,
"height": 900
}
],
"postId": "2108278999638274221",
"postUrl": "https://x.com/cryptomoza92/status/2108278999638274221",
"author": { "username": "cryptomoza92", "verified": true }
},
"meta": { "requestId": "..." }
}
The response headers include x-credits-charged: 2.
Python: save an Article as Markdown
import os
import re
import requests
API = "https://api.weirapp.io"
HEADERS = {"Authorization": f"Bearer {os.environ['WEIR_API_KEY']}"}
def post_id_from(url_or_id: str) -> str:
"""Accepts a post id or an x.com/.../status/<id> URL. Not an /i/article/ link."""
match = re.search(r"/status/(\d+)", url_or_id)
return match.group(1) if match else url_or_id
def save_article(url_or_id: str) -> str:
post_id = post_id_from(url_or_id)
r = requests.get(f"{API}/v1/x/posts/{post_id}/article", headers=HEADERS, timeout=30)
if r.status_code == 404:
raise ValueError(f"{post_id} doesn't exist or doesn't carry an Article")
r.raise_for_status()
article = r.json()["data"]
filename = re.sub(r"[^a-z0-9]+", "-", article["title"].lower()).strip("-") + ".md"
with open(filename, "w", encoding="utf-8") as f:
f.write(f"# {article['title']}\n\n")
f.write(f"By @{article['author']['username']}, {article['publishedAt']}\n")
f.write(f"Source: {article['postUrl']}\n\n")
f.write(article["markdown"] or "")
print(f"Saved {filename} ({len(article['media'])} images/videos, "
f"{r.headers.get('x-credits-charged')} credits)")
return filename
save_article("https://x.com/cryptomoza92/status/2108278999638274221")
Images stay as remote pbs.twimg.com URLs in the Markdown. If you need them offline, download each media[].url.
Finding Articles first, then reading them
To collect Articles on a topic, search, keep the posts whose article isn't null, and fetch each one. Search results only carry the preview, so the second call is what gets you the body:
def articles_about(topic: str, limit: int = 20) -> list[dict]:
r = requests.post(
f"{API}/v1/x/search",
json={"query": topic, "onlyLinks": True, "sort": "top", "limit": limit},
headers=HEADERS,
timeout=30,
)
r.raise_for_status()
hits = [p for p in r.json()["data"] if p.get("article")]
full = []
for post in hits:
a = requests.get(f"{API}/v1/x/posts/{post['id']}/article", headers=HEADERS, timeout=30)
if a.ok:
full.append(a.json()["data"])
return full
onlyLinks narrows the search to posts containing a link. Article posts pass it, because their text is a link (we checked: a fromUsers search with onlyLinks returned that author's Article posts, each with article.text set to null). That's a filter, not an Article-only switch, so the article check is still needed. Every post the search returns costs a credit whether it turns out to be an Article or not, so keep limit modest.
With an AI assistant: the get_x_article MCP tool
Weir's MCP server exposes the same endpoint as the tool get_x_article. MCP tools are named after the API operation in snake_case, so getXArticle becomes get_x_article. Once Weir is connected to Claude, ChatGPT, Cursor, or another client (setup is in this guide), you can paste a link:
Summarise this X Article in five bullet points and list every company it mentions:
https://x.com/cryptomoza92/status/2108278999638274221
The assistant calls get_x_article with the URL and works from the markdown field. Without the tool, it would only see the t.co link. get_x_post also works here: its description tells the model that an Article's body is in article.
Things to keep in mind
- Copyright stays with the author. Reading an Article through an API doesn't change who owns it. Summarising or analysing one is different from republishing it. If you republish, link to the original and check X's terms and the author's rights. This isn't legal advice.
- Subscriber-only Articles aren't public. Weir reads public data only, so an Article limited to subscribers won't come back.
- Edits. An author can unpublish an Article, edit it, and republish it.
updatedAttells you when it last changed, so re-fetch if freshness matters.
Doing this with Weir
Sign up and you get 5,000 free credits a month with no card. At 2 credits each, that's roughly 2,500 Article reads. The endpoint reference is at getXArticle, and rates are on pricing.
Sources
- X Help Center, About Articles: https://help.x.com/en/using-x/articles
- X API data dictionary (
articlefield): https://docs.x.com/x-api/fundamentals/data-dictionary - X API Articles endpoints (draft and publish): https://docs.x.com/x-api/articles/introduction
- X API pay-per-usage pricing: https://docs.x.com/x-api/getting-started/pricing
- X Developer Community thread on Article text: https://devcommunity.x.com/t/for-x-articles-and-long-form-posts-which-tweet-fields-return-the-full-body-on-get-2-tweets/275354
- Weir API reference, getXArticle: https://docs.weirapp.io/reference/x/getXArticle
- Example Article: https://x.com/cryptomoza92/status/2108278999638274221