X API usage: the dollar figure the API withholds
Two endpoints report X API usage. One counts Posts, one counts dollars left, and neither tells you what you actually spent. One is missing from the docs index.
On this page · 9 sections
The short version
- ▸There are two usage endpoints in spec version 2.174. GET /2/usage/tweets reports Posts consumed. GET /2/usage/credits reports dollars remaining. Neither reports dollars spent, and no endpoint breaks spend down by route.
- ▸GET /2/usage/credits is absent from docs.x.com/llms.txt and from the x-api section index. Its reference page exists but is only reachable from one table on the Usage overview page, and the string credits appears zero times in the official Python SDK.
- ▸The response example X publishes for /2/usage/tweets does not match its own API description. It shows daily_project_usage as an array and names the fields app_id and tweets_consumed. The spec has an object, client_app_id and usage, and the generated SDK agrees with the spec.
- ▸total_balance is clamped at zero, so it cannot tell you how far overdrawn you are. prepaid_balance can go negative, requests are blocked until you cover it, and auto-recharge does not run while the balance is at or below zero.
- ▸GET /2/usage/tweets is capped at 50 calls per 15 minutes per app with no per-user allowance. That is the same ceiling X puts on opening a filtered stream connection, and one sixth of what a normal lookup route gets.
Quick answer
X ships two usage endpoints and neither one answers "what did I spend." GET /2/usage/tweets counts Posts consumed. GET /2/usage/credits counts dollars remaining. There is no endpoint that reports dollars spent, and none that breaks spend down by route, so per-endpoint cost attribution is a Developer Console screen and nothing else. The credits endpoint is also effectively hidden: it has no entry in X's documentation index and the string credits appears zero times in the official Python SDK.
Last updated: November 2026
Every number below is checked against the live API description at api.x.com/2/openapi.json, version 2.174, X's usage, billing, pricing and rate-limit pages, and the published Python SDK wheel. All read on 9 November 2026.
Two endpoints, two units
| Route | Returns | Unit | Auth in the spec |
|---|---|---|---|
GET /2/usage/tweets | Posts consumed, by day and by app | Posts | Bearer only |
GET /2/usage/credits | Balance remaining | USD | OAuth 2.0 user or Bearer |
That is the whole surface. Put them side by side and the gap is obvious: one meter counts things, the other counts money, and nothing converts between them. You can learn that your project consumed 41,000 Posts last week and that you have $12.60 left. You cannot learn, from the API, that the user-lookup job cost $9 of it.
Auth splits here too, and it matters. /2/usage/tweets accepts only an app-only Bearer token, which makes sense for a project-scoped meter. /2/usage/credits accepts an OAuth 2.0 user token or a Bearer token, and notably not the OAuth 1.0a user token that most other routes still allow. If you are deciding which credential to mint, how to get an X API key covers the three flows.
The credits endpoint is real, and nearly unfindable
GET /2/usage/credits is in the live spec with a written description, and its reference page renders at docs.x.com/x-api/usage/get-usage-credits. Three things make it hard to reach:
docs.x.com/llms.txt, the index X publishes for machine readers, contains nox-api/usageentries at all.- The
x-apisection index lists exactly two usage pages, "Get Usage" and the "Usage" overview. The credits reference is not one of them. - X's pricing page, the canonical billing document, says your credit balance "is displayed in the Developer Console" and never names the endpoint.
Its only published link is one row in a two-row table on the Usage overview page. And the official SDK has not caught up: in xdk 0.10.6 the usage client exposes a single method, get, bound to /2/usage/tweets, and the string credits appears zero times in the entire package. That dates the endpoint to after 25 July 2026, which is the same pattern described in X API Python for every field that lands between SDK releases. Call it with plain HTTP.
What comes back is more useful than a single number:
| Field | Meaning |
|---|---|
total_balance | Spendable USD, defined as max(0, prepaid_balance + free_balance) |
prepaid_balance | Purchased USD. May be negative when paid usage has overdrawn it |
free_balance | USD left across unexpired free grants |
free_grants | Array of {amount, expires_at}, soonest expiry first |
All four are required fields, so you always get the full picture.
The clamp that hides an overdraft
total_balance bottoms out at zero. prepaid_balance does not. X's pricing page states that a balance can go "slightly negative," that API requests are blocked until you add credits to cover it, and separately that auto-recharge "does not run while your account balance is zero or negative."
Chain those three facts and you get a failure mode worth coding against. A monitor that alerts on total_balance == 0 tells you that you are blocked but not by how much, because the clamp has thrown away the deficit. Auto-recharge will not dig you out, because it is paused in exactly that state. Read prepaid_balance to size the top-up, and treat any negative value there as requiring a manual purchase rather than waiting for the trigger threshold to fire.
That free_grants array is the other reason to call this endpoint rather than read a dashboard. Free credits expire three months after they are granted and are spent before purchased credits, so a balance of $70 can be mostly air if the grants lapse next week. The array gives you each grant's remaining amount and its expires_at, soonest first, with expires_at simply omitted when a grant never expires or the date is unknown. X API pricing covers how those grants are earned and the 13 October 2026 change to the eligibility rule.
The Posts meter, and the example that does not match it
Two parameters shape the Posts meter. days is an integer between 1 and 90 and defaults to 7. usage.fields is a comma-separated list drawn from six values:
cap_reset_day, daily_client_app_usage, daily_project_usage,
project_cap, project_id, project_usage
Here is where it gets awkward. The response example X publishes on the Usage overview page disagrees with X's own API description in four ways:
| X's example | The spec, version 2.174 |
|---|---|
daily_project_usage as an array | An object, with project_id and a nested usage array |
app_id | client_app_id, on daily_client_app_usage |
tweets_consumed | usage, typed as a nullable string |
project_cap: 10000000 (number) | project_cap typed as a string |
X's example also omits cap_reset_day, project_usage and the entire daily_client_app_usage branch, and the field table printed underneath it repeats app_id and tweets_consumed as if they were real.
Once again the SDK settles which one to trust, because it is generated from the same description the docs site renders. In xdk 0.10.6, tweets_consumed appears zero times and client_app_id appears five. Write your parser against the spec, parse the daily counts as strings, and expect nulls.
One more detail on that page: the project_cap in the example reads 10,000,000, while a note at the top of the same page states that pay-per-usage plans are capped at 3 million Post reads a month. Treat the 3 million as the published figure and your own project_cap value as the authority for your project.
Worth checking the search results before you copy anything: the pages ranking for this query reproduce app_id and tweets_consumed verbatim, because they were written from X's example rather than from the description. That is the same reseller-blog pattern that shows up across API topics, and the fix is always to read the spec.
50 calls per 15 minutes
X's rate-limit page has a section headed "Usage (1 endpoint)" with a single row:
| Route | Per app | Per user |
|---|---|---|
GET /2/usage/tweets | 50/15min | no row |
Fifty per fifteen minutes is tight for a monitoring endpoint. It is the same ceiling X puts on opening a filtered stream connection, and one sixth of the 300 per 15 minutes a normal lookup route gets. In practice it means a poll every 20 seconds fits with room to spare and a poll every 15 seconds does not. Pull it on a one-minute timer and cache, especially if more than one process wants the number.
There is no rate-limit row anywhere for GET /2/usage/credits on that page, which is the same gap documented for other recently shipped routes in X API rate limits. Treat it as unpublished rather than unlimited and poll it conservatively.
What counts toward the cap
Seven categories of endpoint draw down the 3 million, per the billing page:
Post lookup, recent and full-archive search, filtered stream and filtered stream webhooks, user Posts and user mentions timelines, liked Posts and bookmarks, List Posts, and Spaces lookup.
Two things follow. Posts arriving through a filtered stream webhook are billed as Post reads against this cap rather than as webhook events, which is the split covered in X API webhooks. And reads that are not Post reads, including every user, DM and analytics read, do not appear in /2/usage/tweets at all. If your spend is mostly profile lookups, the Posts meter will read near zero while your balance drains.
Deduplication softens the cap more than people expect: a Post retrieved several times inside one 24-hour UTC window counts once, which X describes as a soft guarantee rather than a promise, with service outages as the stated exception. And the billing FAQ answers one question the rest of the docs dodge: failed requests are not billed, because "only successful responses that return data are billed."
How to actually audit a bill
Given the above, a workable routine looks like this:
- Call
/2/usage/creditson a schedule and alert onprepaid_balance, nottotal_balance. - Call
/2/usage/tweets?days=30with all six fields to see which client app is burning the Post cap. - Tag each job with its own counter in your own code, because X will not attribute dollars to a route for you.
- Set a spending limit per billing cycle in the Console, which blocks requests when reached, before you run anything in a loop.
Step three is the one people skip and regret. The deduplication window and the per-resource rate card mean two jobs reading overlapping data cost less than the sum of their parts, so a local estimate drifts from the invoice. The Console is the reconciliation surface. The API is the alarm.
When metering is the wrong problem
If you are running a data pipeline, a dashboard, or anything reading Posts at volume, these endpoints are the difference between a surprise and a budget, and the credits route is worth the extra HTTP call even though no SDK exposes it yet.
If what you want is your own account posting, replying and following on a schedule, you are metering the wrong thing. The reads you would pay for are a feed X renders for free in a browser, and the write side is where the API gets restrictive rather than merely expensive. X-Autopilot runs the browser route instead: your own logged-in session, in your own browser, on your own Mac, so the session cookie never leaves the machine and no cloud IP pool or delegate account sits in the path. That is a lower detection surface than a cloud scheduler rather than a safe one, and automating engagement through a browser is a gray area that X's automation rules address directly. The API is the sanctioned route. If you take it, meter it from day one.
Frequently asked
Answers indexed by Google + AI assistants.
How do I check my X API usage?+
GET https://api.x.com/2/usage/tweets with an app-only Bearer token. It takes a days parameter between 1 and 90, defaulting to 7, and a usage.fields parameter with six values: cap_reset_day, daily_client_app_usage, daily_project_usage, project_cap, project_id and project_usage. It reports Posts consumed, by day and by client app. For money, call GET /2/usage/credits instead.
Can I get my X API credit balance from the API?+
Yes. GET /2/usage/credits returns total_balance, prepaid_balance, free_balance and free_grants, all in USD and all required fields. It accepts an OAuth 2.0 user token or an app-only Bearer token. X's pricing page never mentions it and points you at the Developer Console instead, and it has no entry in the documentation index, so most tooling does not know it exists.
What does the X API usage endpoint actually return?+
Per the live API description: cap_reset_day as an integer, project_cap, project_id and project_usage as strings, daily_project_usage as an object holding a project_id and an array of date and usage pairs, and daily_client_app_usage as an array of per-app entries each carrying client_app_id, a usage array and a usage_result_count. Daily usage values are strings, not numbers, and are nullable.
Why does my credit balance show zero when requests are still blocked?+
Because total_balance is defined as max(0, prepaid_balance + free_balance), so it bottoms out at 0.00 and hides an overdraft. X states a balance can go slightly negative and that requests are blocked until you add enough to cover it. Read prepaid_balance to see the real number, since that field is the one allowed to be negative, and note that auto-recharge is paused while the balance is at or below zero.
What is the 3 million Post cap?+
Pay-per-usage accounts are capped at 3 million Post reads per monthly billing cycle, separate from per-endpoint pricing and separate from rate limits. Posts count once per 24-hour UTC window no matter how many times you retrieve them, which X calls a soft guarantee rather than a promise. The confusing part is that X's own usage example shows a project_cap of 10,000,000 on the same page that states the 3 million figure.
Do failed X API requests cost money?+
No. X's Usage and Billing page answers this directly: only successful responses that return data are billed. That makes retry loops on 4XX and 5XX responses cheap in credit terms, though they still consume rate limit. It does not resolve every case, because a request that succeeds and returns zero resources is a different thing from one that fails.
- X live OpenAPI description at api.x.com/2/openapi.json, version 2.174: GET /2/usage/tweets with days bounded 1 to 90 and defaulting to 7 and BearerToken as its only security option, GET /2/usage/credits accepting OAuth2UserToken or BearerToken, the six-value usage.fields enum, the Usage schema typing project_cap and project_usage as strings and cap_reset_day as an integer, UsageDailyProjectUsage as an object and UsageDailyClientAppUsage as an array of client_app_id entries, and GetUsageCreditsResponseData requiring total_balance, prepaid_balance, free_balance and free_grants with total_balance defined as max(0, prepaid_balance + free_balance) (accessed 9 November 2026)
- X developer docs, 'Usage': the two-row endpoint table that is the only published link to the credits reference page, the response example showing daily_project_usage as an array with app_id and tweets_consumed and a project_cap of 10000000, the note stating the 3 million monthly Post read cap, and the API Reference card pointing at usage/post-usage which redirects to the Get Usage page (accessed 9 November 2026)
- X developer docs, 'Usage and Billing': pay-per-usage tracked at the app level, the seven-category table of endpoints whose Posts count toward the cap including filtered stream webhooks, the 24-hour deduplication rule, the Developer Console as the stated place to see cost tracking and credit balance, and the FAQ answer that only successful responses returning data are billed (accessed 9 November 2026)
- X developer docs, 'X API pay-per-usage pricing and credits': the credit balance section stating the balance is displayed in the Developer Console with no mention of an endpoint, the note that a balance can go slightly negative and requests are blocked until it is covered, and the auto-recharge rule that it does not run while the balance is zero or negative (accessed 9 November 2026)
- X developer docs, 'X API Rate Limits': the section headed 'Usage (1 endpoint)' listing GET /2/usage/tweets at 50/15min per app with no per-user column value and no row anywhere for GET /2/usage/credits (accessed 9 November 2026)
- xdk 0.10.6 for Python, published 25 July 2026, read from the wheel: the usage client exposes a single method, get, bound to /2/usage/tweets with days and usage_fields; the string credits appears zero times in the package and tweets_consumed appears zero times, while client_app_id appears five times (accessed 9 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 →