WEIR

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.

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 article as 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. updatedAt tells 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