One handle in. Clean JSON out.
Give this endpoint an X (Twitter) handle and it returns that account's recent public posts as a JSON array — text, engagement counters, timestamps, language, and reply/retweet flags. It bills 1 credit per tweet returned, so a 20-tweet pull costs 20 credits. No developer account, no OAuth, no X API tier to negotiate.
The difference
On the left, what a headless browser hands you. On the right, what the endpoint hands you.
<article data-testid="tweet" class="css-175oi2r r-1adg3ll r-1ny4l3l"><div class="css-175oi2r r-18u37iz"><div dir="ltr" class="css-901oao r-1nao33i">Shipping notes: response times are down 40% after this week's cache work.</div></div><div role="group" aria-label="90211 views, 1211 likes, 204 reposts" class="css-175oi2r r-1kbdv8c"><div data-testid="like"><span class="css-1jxf684">1,211</span></div><div data-testid="retweet"><span>204</span></div></div><time datetime="2026-07-06T08:15:00.000Z">8h</time><script>window.__INITIAL_STATE__={"entities":{"tweets":{"entities":{"186112... {"data": [{"id": "1861122334455667788","url": "https://x.com/XDevelopers/status/1861122334455667788","text": "Shipping notes: response times are down 40% after this week's cache work.","posted_at": "2026-07-06T08:15:00Z","metrics": {"views": 90211,"likes": 1211,"reposts": 204,"replies": 88}}],"meta": {"count": 2,"limit": 20,"item": "tweet"},"request_id": "req_01JZX4M8Q2TE9W"}
The payload
An array of tweet objects for the handle you passed, in whatever order the public timeline hands them to us. We do not re-sort, and that order is not guaranteed to be strictly newest-first, so sort on posted_at yourself if recency matters. Each object carries the post text, the five public counters X exposes (views, likes, replies, retweets, quotes) plus bookmarks, the publish timestamp, and a small nested author block so rows stay self-describing after you flatten them into a table.
The array includes replies and retweets when the account posted them. That is deliberate — filtering them out server-side would hide activity some people are specifically measuring. Two boolean flags, is_reply and is_retweet, let you split the timeline into original posts versus conversation on your side.
Control how many you get with the limit parameter; the default is 20. Because billing is per tweet, limit is also your cost dial. Billing follows what comes back, not what you asked for: request 100 from a handle that has only posted 12 and you are charged 12.
Likes as X displayed them when the call ran. On a retweet row this reflects the underlying post, not the retweet.
Pricing
Each tweet returned is 1 credit — about 0.5¢ on the monthly plan. A default 20-tweet pull runs 10¢, and a handle that has never posted still costs 1 credit.
Estimate only, on a full-year basis. 1 credits per returned tweet. Monthly: $5 per 1,000 credits with $4/1,000 top-ups. Annual: $54 per 12,000 credits with $3/1,000 top-ups. We show whichever plan is cheaper for your annual volume, divided by 12. Failed calls are never charged; cache hits cost the same as fresh calls.
Entry price, side by side
Credits are not equivalent units across vendors — each meters differently. Price your own workload on both before deciding on entry price alone. At high volume, several of these are cheaper per credit than we are.
Engineering notes
We document exactly how every endpoint behaves — nulls, caching, billing — so what you ship on day one is still running in month six. No surprises, no support tickets.
Billing is per tweet returned, at 1 credit each. A default call costs 20 credits, about 10 cents on the monthly rate. Set limit to the number you will actually use — pulling 200 posts to display 10 is the single most expensive mistake on this endpoint.
If a handle has fewer public posts than your limit, you are charged for what comes back, not what you asked for. An account with 7 posts and limit=100 costs 7 credits.
A busy account's recent 20 posts may be mostly replies. If you are measuring original output, filter with is_reply === false && is_retweet === false and raise limit to compensate, because the filtering happens after you have paid for the rows.
Retweets carry the retweeting account in author, and their counters reflect the underlying post. Mixing retweets into an average engagement figure will skew it.
views was not shown by X for years, so historical posts commonly return null there while likes and shares are populated. bookmarks is inconsistent too. Treat null as "not published" rather than zero — charting a null as 0 invents a data point.
text is null on posts that are only an image or video with no caption. url can be null; the id never is.
We read what a logged-out browser can read. A protected account returns nothing usable, and neither does a suspended or deleted one. We do not sign in, so there is no way around that and no partial data to offer.
A handle that exists but has never posted returns an empty array. That is a successful call, not an error, and it bills the one-credit floor that applies to any 2xx returning no items.
Repeated calls for the same handle inside the cache window return from cache in milliseconds and set x-cache: hit. Caching buys you speed and nothing else — a hit still bills 1 credit per tweet in the cached array, so a polling loop that keeps hitting cache is paying full price for posts it has already seen. Pass fresh=true on a poll so you do not miss a new post.
Questions
Send a GET to /v1/twitter/tweets with the handle and your API key. There is no X developer account, no app review and no OAuth dance. X's own API now gates timeline reads behind paid tiers with monthly post caps, which is why most side projects and internal dashboards cannot use it. This endpoint reads public posts logged out and returns JSON.
1 credit per tweet, so 1000 tweets is 1000 credits — $5 on the monthly rate, $4.50 on annual. The cost is driven by the limit parameter, not by how many calls you make, so ten calls of 100 and one call of 1000 cost the same. Failed calls are not charged.
Only as far as one call reaches. Raising limit is the only lever — there is no cursor, no page token and no way to resume where a previous call stopped, so a second call simply re-fetches from the top rather than continuing deeper. How much a single call reaches varies by account, because X caps what a logged-out visitor can see on a public profile, and very old posts are usually unreachable this way. If you need a specific historical post, fetch it by its own URL.
Yes. Both appear in the array with is_reply and is_retweet set, and both are billed like any other tweet. We do not filter them out server-side because plenty of use cases — support monitoring, conversation tracking — care about exactly those rows. Filter client-side on the two booleans if you only want original posts.
X only started publishing view counts in late 2022, and it still does not show them everywhere. Posts from before that return null for views while likes, shares and quotes are present. Store null rather than coercing it to zero, otherwise any average or chart you build will quietly understate engagement on your older rows.
No. Protected accounts are invisible to logged-out visitors and this endpoint reads exactly what a logged-out visitor reads. Suspended and deleted accounts return nothing too. We do not authenticate, bypass logins or work around access controls — public content only, which is a deliberate boundary rather than a missing feature.
Call with a modest limit — 20 is usually plenty — on your polling interval, pass fresh=true to skip the cache, and keep the highest posted_at you have already stored. Insert only ids you have not seen. Small frequent pulls are far cheaper than large ones, since you pay per tweet returned every time.
Next
100 trial credits on signup — no card, key on screen immediately.