X API Rate Limits: Per-Endpoint Limits and 429 Fixes
X API v2 rate limits are set per endpoint, usually per 15-minute window. POST /2/tweets allows 100 posts per user every 15 minutes and 10,000 per app every 24 hours. Every response carries x-rate-limit-limit, x-rate-limit-remaining, and x-rate-limit-reset headers. Going over returns 429 with error code 88: wait for the reset time, then retry with jitter. Adeli runs the X app and refreshes tokens for you.
X API rate limits cap how many requests you can make to each endpoint in a time window, usually 15 minutes. For posting, POST /2/tweets allows 100 requests per user per 15 minutes and 10,000 per app per 24 hours. Every response tells you where you stand through three x-rate-limit-* headers, and going over returns 429 Too Many Requests until the window resets.
This post lists the limits by endpoint, shows how to read the headers, and gives retry code for a 429. Every number comes from X's rate limits page, checked on 2026-10-26. For the wider picture of the API, start with our X API guide.
How do X API rate limits work?
X API rate limits are counted per endpoint, per time window, and per app or per user depending on how you authenticate. The four rules from X's docs:
| Concept | How it works |
|---|---|
| Time window | Usually 15 minutes; some limits are per 24 hours or per second |
| Per-user limits | Apply when you call with an OAuth 1.0a or OAuth 2.0 user token |
| Per-app limits | Apply when you call with the app-only Bearer Token |
| Per endpoint | Each endpoint has its own counter |
The two pools don't drain each other. X's app-only auth guide says requests made with user tokens come from a different rate-limit pool than requests made with the Bearer Token.
What is the X API post rate limit?
POST /2/tweets allows 100 posts per user per 15 minutes and 10,000 posts per app per 24 hours. Deleting is limited separately.
| Method | Endpoint | Per app | Per user |
|---|---|---|---|
POST |
/2/tweets |
10,000 / 24 hours | 100 / 15 min |
DELETE |
/2/tweets/:id |
Not listed | 50 / 15 min |
POST |
/2/users/:id/retweets |
Not listed | 50 / 15 min |
POST |
/2/users/:id/likes |
Not listed | 50 / 15 min, 1,000 / 24 hours |
POST |
/2/media/upload |
50,000 / 24 hours | 500 / 15 min |
POST |
/2/media/upload/initialize, /append, /finalize |
180,000 / 24 hours | 1,875 / 15 min |
X's own pages give two different per-user numbers. The rate limits page says 100 per 15 minutes. The Manage Posts guide says 200 per 15 minutes, plus a limit of 300 posts per 3 hours that counts posts and reposts together. Build to the lower numbers and read the headers rather than hard-coding either one.
The per-user limit follows the user across apps. The Manage Posts guide says the user can only call the endpoint a set number of times "across any developer App." If a customer also posts through another tool, both draw from the same budget.
What are the X API v2 rate limits for reading?
Read limits are higher than write limits and nearly all reset every 15 minutes. The endpoints social products call most:
| Method | Endpoint | Per app | Per user |
|---|---|---|---|
GET |
/2/tweets |
3,500 / 15 min | 5,000 / 15 min |
GET |
/2/tweets/:id |
450 / 15 min | 900 / 15 min |
GET |
/2/users/:id/tweets |
10,000 / 15 min | 900 / 15 min |
GET |
/2/users/:id/mentions |
450 / 15 min | 300 / 15 min |
GET |
/2/tweets/search/recent |
450 / 15 min | 300 / 15 min |
GET |
/2/tweets/search/all |
1 / sec, 300 / 15 min | 1 / sec |
GET |
/2/tweets/analytics |
300 / 15 min | 300 / 15 min |
GET |
/2/users/me |
Not listed | 75 / 15 min |
GET |
/2/users/by/username/:username |
300 / 15 min | 900 / 15 min |
Direct Messages are the tightest. Looking up DM events allows 15 requests per user per 15 minutes, and sending a message allows 15 per 15 minutes and 1,440 per 24 hours, per user and per app. A support inbox that polls DMs will hit this first.
How do I check my X API rate limit?
Read the three x-rate-limit-* headers that come back on every response. X's error reference says every response includes them:
| Header | Meaning |
|---|---|
x-rate-limit-limit |
The maximum requests allowed in the window |
x-rate-limit-remaining |
Requests left in the current window |
x-rate-limit-reset |
Unix timestamp (seconds) when the window resets |
curl -s -D - -o /dev/null "https://api.x.com/2/users/me" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" | grep -i x-rate-limitThe Developer Console also shows each endpoint's limits. Store the headers per endpoint and per user, so your scheduler knows which accounts can post before it tries.
Why do I get "Rate limit exceeded" from the X API?
You get "Rate limit exceeded" when a window's budget is spent: X returns HTTP 429 with error code 88. The body looks like this:
{
"errors": [{ "code": 88, "message": "Rate limit exceeded" }]
}A 429 isn't always a rate limit. X's error reference lists 429 for "Rate limit or usage cap exceeded", with separate problem types for each:
| Problem type | What happened | Fix |
|---|---|---|
rate-limit-exceeded |
The window's request budget is spent | Wait for x-rate-limit-reset |
usage-capped |
A usage cap is reached | Waiting for the window won't help; check usage and limits in the console |
Two other errors tend to show up next to 429s. 503 Service Unavailable means X is overloaded: wait and retry. And if your credit balance runs out, X blocks requests until you top up, which no retry logic can fix. Billing is covered in X API pricing.
How do I handle a 429 from the X API?
Wait until x-rate-limit-reset, add a random delay, and retry. X's own recovery steps are to check the reset header, wait until then, and use exponential backoff if needed. Here it is in Node.js:
async function xRequest(url, options, attempt = 0) {
const res = await fetch(url, options);
if (res.status !== 429 && res.status < 500) return res;
if (attempt >= 5) throw new Error(`X API gave up after ${attempt} retries: ${res.status}`);
const reset = Number(res.headers.get("x-rate-limit-reset"));
const untilReset = reset ? reset * 1000 - Date.now() : 0;
const backoff = Math.min(60_000, 1000 * 2 ** attempt);
const jitter = Math.random() * 1000;
await new Promise((r) => setTimeout(r, Math.max(untilReset, backoff) + jitter));
return xRequest(url, options, attempt + 1);
}Don't skip the jitter. When many workers hit a limit together, they all wake at the reset time and collide again. Marc Brooker, a Distinguished Engineer at AWS, measured this in Exponential Backoff And Jitter (March 4, 2015):
"The return on implementation complexity of using jittered backoff is huge, and it should be considered a standard approach for remote clients."
Don't retry a post blindly after a timeout. If the post went through and the response got lost, a retry creates a duplicate, which X rejects. Check the account's recent posts before retrying a create.
How do I stay under X API rate limits at SaaS scale?
Queue posts per user, and plan around the per-app daily cap, because every customer shares it. The 10,000-posts-a-day limit belongs to your app, not to each customer. A product with 3,000 connected accounts posting 5 times a day needs 15,000 and won't fit on self-serve.
Paul Tarjan, then an engineer at Stripe, described the two client patterns in Scaling your API with rate limiters (March 30, 2017):
"Fork off X jobs and have them process the queue" compared to "Hammer the API and back off when I get a HTTP 429"
For posting to X, the queue model wins. In practice:
- One queue per connected account. Space each user's posts so they stay under 100 per 15 minutes without ever seeing a 429.
- A global counter for the app. Track creates against the 10,000-per-24-hours cap and slow the whole queue before you reach it.
- Cache reads. Post lookups are deduplicated for billing within a UTC day, but they still count against rate limits. Store what you've read.
- Use streams or webhooks for real-time data. X recommends the filtered stream over polling.
- Ask about Enterprise early. X gives Enterprise customers custom limits, and its terms describe self-serve plans as built for a limited number of end users.
Is there a faster way to post to X at scale?
Yes, if your customers are the ones posting. Rate-limit handling is one of several jobs an X integration carries: the developer app, the OAuth flow, refreshing every account's tokens, and the per-request bill.
Adeli's X API maintains the X developer app, has passed X's review, and refreshes every connected account's tokens. Your product sends one request to POST /api/v1/posts, the same call it uses for Instagram, TikTok, YouTube, Facebook, and Bluesky (posting API). When X is unavailable to Adeli for a while, the response is 503 temporarily_unavailable, and Adeli's error docs say to retry 429 and 502 with backoff. X's API usage fees are passed through at X's published rates.
If you're building social features into a marketing platform, see how Adeli fits AI marketing platforms.