X API expansions: what starts costing on 22 November
X will bill expansion objects from 22 November 2026. Three of the five resource types have never appeared on the public rate card. Here is the new arithmetic.
The short version
- ▸From 22 November 2026, objects returned in the includes section count towards pay-per-use usage at the standard rate for that resource. Until then they are free. X announced this in the developer forum on 23 October 2026 and has not yet put it on the pricing page.
- ▸Media, Poll and Place have never had a row on the public rate card, because none of them is a primary object on any endpoint. The announcement prices all three at $0.005, which creates three new billable resource types that can only ever reach you through an expansion.
- ▸The expensive one is User at $0.010, which is twice a Post. A 100-post search with expansions=author_id goes from $0.50 to as much as $1.50 if every author is distinct, and stays near $0.60 if they are not. Your bill now tracks author diversity, not result count.
- ▸Fields stay free, objects start costing. tweet.fields=entities already gives you every mentioned handle and ID without the entities.mentions.username expansion, and attachments.media_keys without the media expansion.
- ▸Two things X has not documented: whether an expanded object on an Owned Read gets the $0.001 rate, and whether expanded Posts count against the 3 million Post read monthly cap. Both change the sums materially at volume.
Quick answer
X API expansions are free today and billable from 22 November 2026. Objects returned in the includes section will be charged at the standard pay-per-use rate for their type: $0.005 for a Post, $0.010 for a User, and $0.005 each for Media, Poll and Place. X announced it in a forum thread on 23 October 2026, not in the changelog and not on the pricing page. The size of your increase depends on how many distinct objects your expansions return, not on how many requests you make.
Last updated: November 2026
What is actually changing
An expansion is the parameter that saves you a second request. Ask for a post with expansions=author_id and the author's user object arrives in the same response, under includes, instead of costing you a trip to /2/users.
On pay-per-use, usage has been based on the data objects the API returns. X's staff announcement puts the current state plainly: objects that come back through expansions are not counted. From 22 November 2026 they will be, at the same rate as a direct lookup, and the charges appear in the Developer Console from that date.
| Object | Rate per resource |
|---|---|
| Post | $0.005 |
| User | $0.010 |
| Media | $0.005 |
| Poll | $0.005 |
| Place | $0.005 |
X frames this as consistency rather than a price change, and on the wording of the public pricing page it has a point: that page already says reads are "charged per resource returned in the response", which is exactly what an expansion object is. The behaviour was the thing that did not match the documentation. Everyone who built against the behaviour is still going to see a different number in December.
Three of these five rates are new
Compare the announcement table against the read rate card on X API pricing. The public card lists Posts, User, DM Event, Following/Followers, List, Space, Community, Note, Like, Mute, Block and Profile Update.
There is no Media row. No Poll row. No Place row.
That is not an oversight. X's own data dictionary says the media object "is not a primary object on any endpoint", and repeats the same sentence for poll and for place. There has never been a /2/media/{key} to charge you for. These three objects can reach you one way only, through an expansion on a post, which is precisely why they never needed a price.
So the announcement does not only reprice something you were already getting. It creates three billable resource types that did not previously exist anywhere in X's billing model, and it does it in a forum thread.
Media is the rate with teeth. A post with four photos returns four media objects. At $0.005 each that is $0.020 of media attached to a $0.005 post: the attachments cost four times the thing they are attached to. Image-heavy accounts, brand monitoring, anything in fashion or sport or news photography, all get noticeably more expensive on 22 November than a flat reading of the rate table suggests.
Place deserves a flag too. We wrote in X API stream about the geo enrichment developers reported through 2026, where includes.places[] came back carrying country, name and place_type and no geo object at all. From 22 November that degraded place object costs $0.005. Check what geo.place_id is actually returning for you before the date, not after.
The number that moves your bill is author diversity
Here is where most of the panic will be misplaced.
Deduplication still applies. X's pricing page says all resources are deduplicated within a 24-hour UTC day window, and the rule is written for "all billable resources", so once an expansion object is billable the plain reading is that it dedupes alongside everything else. X did not restate that in the announcement, which is worth confirming in the console in the first week.
Run the sums for a 100-post page from X API search with expansions=author_id:
| Result set | Posts | Users billed | Total |
|---|---|---|---|
| Today, any shape | $0.50 | $0.00 | $0.50 |
| 100 posts, 100 distinct authors | $0.50 | $1.00 | $1.50 |
| 100 posts, 20 distinct authors | $0.50 | $0.20 | $0.70 |
| 100 posts, authors already seen today | $0.50 | $0.00 | $0.50 |
Same request, same page size, three different bills. A broad keyword search that returns a hundred strangers triples. A tracker polling the same twenty accounts all day pays for those twenty users once and then nothing.
Breadth is what X prices, and this change does not alter that: X prices breadth. Depth against a fixed set stays cheap. This is also why a List timeline survives the change better than a keyword stream does. List membership is capped at 5,000 accounts you chose, so the worst case for expansions=author_id on a List is 5,000 user objects a day, or $50, and in practice it is the number of members who actually posted.
A keyword stream has no such ceiling, because you do not control who matches.
The expansions to audit first
Not all expansions are equal. Ranked by how badly they can surprise you:
entities.mentions.username. Returns a user object for every handle mentioned in every post. The count is bounded by the text of other people's posts, not by your max_results. On reply-heavy or announcement-heavy data this can return more user objects than you have posts, each at $0.010. Audit this one first.
author_id and referenced_tweets.id.author_id together. On a conversation thread these return two user objects per post, the author and the author of the parent. Dedup absorbs most of the overlap inside a tight thread and almost none of it across a broad search.
attachments.media_keys. Cheap per object, high per post, as above.
edit_history_tweet_ids. The ID list is a default field on the post and costs nothing. The expansion turns that free list into billable Post objects, one per previous version. Most builds that set it do not actually read the old versions.
referenced_tweets.id. On a reply-dense timeline this roughly doubles the Post count. On a timeline of original posts it returns almost nothing.
Fields are free, objects are not
The single most useful rule after 22 November: a field parameter has never been billable, and nothing in the announcement changes that. You are charged for objects in includes, not for the attributes you request on them.
That means a good share of the expansions in production code can simply be deleted:
- Want the handles mentioned in a post?
tweet.fields=entitiesgives youentities.mentionswith theusernameandidof every mention. No user objects, no charge. Use the expansion only if you need something off the user profile itself, like follower counts. - Want to know a post has media?
tweet.fields=attachmentsreturns the media keys. Expand toattachments.media_keysonly when you need the URL, type, duration or alt text. - Want to know a post is a reply or a quote?
tweet.fields=referenced_tweetsreturns the type and ID. Expand only when you need the parent's text.
Three concrete steps before the date:
- Grep your codebase for
expansions=and write down, for each one, the field you actually read off the result. If you cannot name one, delete the expansion. - Cache user objects locally with a refresh interval measured in days. Profiles change slowly; paying $0.010 a day per author to re-learn a display name is the most avoidable line on the new bill.
- Watch the Developer Console daily through the first week of December.
GET /2/usage/tweetsreports daily Post consumption, which will tell you whether expanded Posts are landing against the 3 million Post read monthly cap. X has not said whether they do.
The two answers nobody has
Two questions decide real budgets, and the announcement answers neither.
Do expansions get the Owned Read rate? Owned Reads price your own data at $0.001 instead of $0.005 or $0.010. The qualifying list is defined by endpoint, not by object. If you call GET /2/users/{id}/tweets on your own account with expansions=author_id, is the single user object that comes back $0.001 or $0.010? On a personal dashboard that is the difference between a rounding error and a tenfold increase on the user line.
Do expanded Posts count against the 3 million Post read cap? Pay-per-use is capped at 3 million Post reads per monthly billing cycle, after which you need Enterprise. If referenced_tweets.id now contributes to that counter, a reply-heavy pipeline reaches the ceiling meaningfully sooner than its own read count suggests.
Ask both in the forum thread before 22 November rather than finding out from an invoice. And note where this term is published: not the changelog, which has entries through 29 October 2026 and nothing on this, and not the pricing page. If you are monitoring X's billing terms by watching the docs, as the rate limits page trains you to do, you would have missed this entirely.
Most of this is only a problem if your build reads broadly. If what you want is consistent posting and replying from your own account rather than a data pipeline, X-Autopilot runs on your Mac in your own browser session and never touches a metered read.
Frequently asked
Answers indexed by Google + AI assistants.
What are expansions in the X API?+
Expansions pull related objects into the includes section of a single response, so you do not have to make a second request. expansions=author_id attaches the user object for each post, attachments.media_keys attaches the images or video, geo.place_id attaches the location, and referenced_tweets.id attaches the quoted or replied-to post.
Do X API expansions cost money?+
Not until 22 November 2026. X confirmed in the developer forum on 23 October 2026 that objects returned through expansions are not currently counted towards pay-per-use usage, and that from 22 November they will be billed at the standard rate for that resource: $0.005 for a Post, $0.010 for a User, and $0.005 each for Media, Poll and Place.
How much more will my X API bill be after the change?+
It depends on one thing: how many distinct objects your expansions return. Deduplication within a 24-hour UTC day means a thousand posts by fifty authors bills fifty user objects, not a thousand. A feed of a thousand posts by a thousand different authors bills a thousand. Audit the diversity of your result set, not its size.
Which expansion is the most expensive?+
Any expansion that returns users, because User is $0.010 per resource, double the Post rate. entities.mentions.username is the one to look at first, because it returns a user object for every handle in every post's text, and that count is not bounded by the number of posts you asked for.
Can I get the same data without paying for expansions?+
Often, yes. Field parameters are not billed, only objects are. tweet.fields=entities returns every mention with its username and ID, which is usually what people wanted from entities.mentions.username. tweet.fields=attachments returns the media keys without the media objects. Request the expansion only when you genuinely need fields the parent post does not carry, such as a media URL or a user's follower count.
Is the expansions change on the X pricing page?+
Not as of 6 November 2026. The pricing page still says reads are charged per resource returned in the response and lists no rate for Media, Poll or Place. The only place the change and the five rates are written down is the forum announcement of 23 October 2026, which is a poor place for a billing term to live.
- X Developers forum - '[Inform] X API PPU - Expansions Update' by X staff, posted 23 October 2026: the statement that expansion objects are not currently counted, the 22 November 2026 start date, and the rate table giving Post $0.005, User $0.010, Media $0.005, Poll $0.005 and Place $0.005
- X developer docs - 'X API pay-per-usage pricing and credits': the read rate card with no Media, Poll or Place row, the 24-hour UTC deduplication rule covering all billable resources, the Owned Reads table at $0.001, and the 3 million Post read monthly cap (accessed 6 November 2026)
- X developer docs - 'Expansions': the full list of post, user, Space, DM and List expansions, including entities.mentions.username and edit_history_tweet_ids (accessed 6 November 2026)
- X developer docs - 'Data Dictionary': the statements that the media, poll and place objects are not primary objects on any endpoint and can only be found and expanded inside the post object (accessed 6 November 2026)
- X API changelog: the dated release notes through 29 October 2026, which carry the app-conversions endpoint and the DM refusal error work but no entry for the expansions billing change (accessed 6 November 2026)
Product designer and indie hacker. Runs the agent on his own X account every day and writes up what the data shows, including when it's inconvenient.
Follow on X →