Social Media API Rate Limits: Every Platform Compared
Meta derives your allowance from engagement, YouTube charges weighted units, Bluesky counts points, and X charges money. A single counter will be wrong on most of them. The reference table, the four counting models behind it, and how to build a limiter that survives all four.

Social media API rate limits are not one thing. Meta derives your allowance from how much engagement an account gets, YouTube charges weighted units per method, Bluesky counts points per write, Mastodon counts plain requests per window, and X charges money per call. A single counter across platforms will be wrong on at least three of them. This is the reference table, the four counting models behind it, and how to build a limiter that survives all of them.
The reference table
Verified against official documentation. Where a figure is not published, that is stated rather than guessed.
| Platform | Limit | Window | Scoped to |
|---|---|---|---|
| Facebook Pages | 4800 × engaged users | 24 h | App, via Page or system user token |
| Facebook (app token) | 200 × users | 1 h | App |
4800 × impressions | 24 h | App–user pair | |
| Threads | 4800 × impressions, minimum 10 impressions | 24 h | App–user pair |
| YouTube | 10,000 units (cost-weighted) | Day, resets midnight Pacific | Project |
| Bluesky (writes) | 5,000 points/h, 35,000 points/day | Rolling | Account (DID) |
| Bluesky (API) | 3,000 requests | 5 min | IP |
| Mastodon | 300 requests | 5 min | Account and IP, separately |
| Mastodon (media) | 30 uploads | 30 min | Account |
| Discord | Per-route buckets + a global limit | Varies | Bot, route, and top-level resource |
| TikTok | QPS caps at app, endpoint and advertiser level | Per second | App / advertiser |
| X | 2M post reads/month cap; billed per call | Month | Account |
| Not published | - | App and member | |
| Published per-endpoint; e.g. 5,000 calls/min per ad account on some endpoints | Minute | Ad account |
Two entries deserve a warning label.
Instagram's publishing cap is a moving number. Rather than hardcoding it, query it:
curl -G "https://graph.facebook.com/v26.0/${IG_USER_ID}/content_publishing_limit" \ -d "access_token=${TOKEN}"
Meta ships an endpoint specifically so you do not have to guess - and the reason is that Meta's own documentation contradicts itself. On a single page of the Content Publishing guide, one section states accounts are limited to 100 API-published posts in a rolling 24-hour period, while another states 50. The content_publishing_limit endpoint returns the number that is actually enforced for that account.
So every blog post quoting a fixed publishing cap is quoting a snapshot of a figure the platform's own docs disagree on. Query it. The container flow this cap applies to, and what it does to a fleet of connected accounts, is covered in our Instagram Graph API guide.
Discord tells you explicitly not to hardcode anything. Their documentation states that limits depend on many factors and are subject to change, and that apps should parse response headers instead. Take them at their word.
Four counting models
Once you see the models, the table stops looking arbitrary.
1. Requests per window
The obvious one. N requests per T minutes. Mastodon (300 per 5 minutes), Bluesky's PDS layer (3,000 per 5 minutes per IP), Pinterest per endpoint.
Easy to reason about, easy to implement, and the only model most generic "API rate limiting" articles describe.
2. Cost-weighted quota
YouTube gives a project 10,000 units per day, but methods cost different amounts. videos.insert costs 1,600 units - so a naive reading of "10,000 per day" suggests plenty of headroom, while the reality is about six uploads.
Two multipliers people miss: paginated responses cost the quota per page, not per logical query, and Live Streaming API methods draw from the same pool.
Bluesky uses the same idea with different units: writes cost points - CREATE 3, UPDATE 2, DELETE 1 - against 5,000 points per hour. That works out to roughly 1,666 record creations per hour.
3. Derived from engagement
This is Meta's model, and it is the one that breaks people's mental models entirely.
Facebook Pages: 4800 × engaged users per 24 h Instagram: 4800 × impressions per 24 h Threads: 4800 × impressions per 24 h (minimum 10 impressions)
Your allowance is a function of how popular the account is, not of what you pay or which tier you are on.
The consequence is counter-intuitive and operationally painful: a brand-new account has almost no budget. Exactly when your onboarding wants to backfill a content calendar for a new client, the limit is at its tightest. A large account you have barely touched, meanwhile, has enormous headroom.
Threads at least sets a floor - impressions are treated as a minimum of 10 - so a brand-new Threads account gets 48,000 calls rather than zero. Facebook Pages and Instagram have no such floor documented.
Capacity planning per customer, not per app, is the only thing that works here. And you cannot compute it in advance: you have to observe it. The header mechanics behind this model, and the two time fields that throttle you before the call count does, are in our Facebook Graph API guide.
4. Money
X's pay-per-use model has no calls-per-hour limit in the traditional sense. You are billed per operation, with a 2 million post-read monthly cap on pay-per-use. The limiter is your budget, and the failure mode is not a 429 - it is an invoice.
That changes what your throttle is protecting. Everywhere else you are protecting availability; on X you are protecting spend. Our breakdown of the numbers is in X (Twitter) API pricing.

Response headers, side by side
Reading headers is how you stay under a limit instead of discovering it. Every platform does it differently.
| Platform | Headers | What they carry |
|---|---|---|
| Meta (FB/IG/Threads) | X-App-Usage | JSON: call_count, total_time, total_cputime - each a percentage, 0–100 |
| Meta Ads | X-Ad-Account-Usage | acc_id_util_pct, reset_time_duration, ads_api_access_tier |
| Discord | X-RateLimit-Limit, -Remaining, -Reset, -Reset-After, -Bucket, -Global, -Scope | Counts and a bucket ID |
| Mastodon | X-RateLimit-Limit, -Remaining, -Reset | Counts; reflects the limit you are closest to exceeding |
| Bluesky | Standard draft rate-limit headers | Counts |
| X / LinkedIn / TikTok / Pinterest | Vary by endpoint | Rely on 429 and documented per-endpoint limits |
Three practical notes that cost real time.
Meta's header is percentages, not counts. call_count: 28 means you have used 28% of your allowance, not that you have made 28 calls. Teams that treat it as a count discover the mistake at 100.
The two time fields matter as much as the call count. You can sit at call_count: 12 and still be throttled because total_cputime reached 100 - which is what happens when you request wide field sets over large edges. Trimming requested fields is the fix, and it is not a micro-optimisation.
Discord's X-RateLimit-Bucket is the real key. Several endpoints can share one bucket, and the same endpoint with different top-level resources (channel_id, guild_id, webhook_id) can have independent limits. Keying your counter on the URL path gives you the wrong answer in both directions. Key it on the bucket.
Mastodon returns overlapping limits. An endpoint can be subject to several at once; the headers describe whichever you are nearest to breaching. So the numbers you see can jump between limits without your traffic changing shape.
Publishing limits are separate
Request limits and publishing limits are different ceilings, and hitting the second one is more visible to your customers.
| Platform | Publishing constraint |
|---|---|
A documented per-24 h cap - query content_publishing_limit rather than assuming | |
| Mastodon | Media uploads: 30 per 30 minutes. Deletes and un-reblogs: 30 per 30 minutes |
| Bluesky | Writes cost points; ~1,666 creations per hour |
| YouTube | videos.insert at 1,600 units means roughly six uploads per day on default quota |
| TikTok | Per-second QPS at app, endpoint and advertiser level |
A scheduler that respects request limits and ignores these will pass every load test and then fail on a customer's launch day, when twelve posts go out in one hour.
fifteen models, one limiter. We built it - you can just use it
Building one limiter over four models
You cannot normalise these into a single counter. What you can normalise is the decision: may I make this call now?
Model each platform as a budget with its own unit. Requests, units, points, percent, or dollars. The limiter's interface is canSpend(platform, account, cost); the unit lives inside the implementation.
Track per account, not per app. Meta's engagement-derived model makes app-level counters meaningless - two customers on the same app have completely different ceilings. Your key is (platform, account).
Trust headers over your own arithmetic. Your counter drifts: retries, parallel workers, calls from other parts of your system. The header is the platform's own view. Where one exists, reconcile to it after every response and treat your local count as an optimistic estimate between calls.
after each response: if platform exposes usage headers: budget[account] = parse(headers) # authoritative else: budget[account] -= cost(request) # estimate
Back off with jitter, and respect Retry-After when given. Discord returns retry_after in seconds as a float - use it rather than your own guess. Exponential backoff without jitter turns one throttled worker into a synchronised thundering herd.
Classify errors before retrying. A 429 is retryable. A permission error is not, and retrying it burns budget to produce the same failure. This is the most common way teams turn a small problem into an outage - the retry-classification section of our social media API integration guide covers the policy in full.
Queue with per-account fairness. One customer backfilling 500 posts should not starve everyone else. Round-robin across accounts rather than draining a global FIFO.
What this costs you to maintain
Every number in the table above has changed at least once in the last two years. Meta changed Instagram's publishing cap. X replaced tiers with pay-per-use. Versioned APIs sunset on rolling schedules.
A rate limiter is not a build-once component. It is a surface that needs watching, and the watching is the expensive part.
bundle.social runs one limiter across every platform we support, tracks per-account budgets, reconciles against platform headers, and queues with per-account fairness. When a platform changes its maths, we change ours. Your integration keeps calling one endpoint.
FAQ
Why is my Instagram rate limit lower than another account's?
Meta derives the allowance from impressions: 4800 × impressions per 24 hours. A lower-reach account gets a smaller budget. It is a property of the account, not your app tier.
What does call_count mean in Meta's X-App-Usage header?
It is the percentage of your allowance consumed, 0–100 - not a count of calls. Throttling begins at 100, and the same applies to total_time and total_cputime.
How many videos can I upload to YouTube per day?
The default project quota is 10,000 units per day and videos.insert costs 1,600, so roughly six uploads before other calls are considered. Quota resets at midnight Pacific.
Do batched requests use less quota? Generally no. On Meta's Graph API each call inside a batch counts separately for both call limits and CPU limits. Batching reduces round trips, not consumption. Field expansion genuinely reduces call count.
Should I hardcode rate limits from documentation?
No. Discord's docs say so explicitly, and every other platform has changed its numbers. Read response headers where they exist, query live endpoints like Instagram's content_publishing_limit, and treat 429 as authoritative.