Handling social API rate limits at scale
What changes about rate-limit handling once you're publishing across hundreds of connected accounts instead of one.
At scale, rate limiting becomes a scheduling problem, not a retry problem. Platforms layer per-account and per-app counters, and with hundreds of connected accounts the shared app-level ceiling binds first. Gate outbound requests with a token-bucket limiter per platform and app credential, partition queues by destination, prefer webhooks over polling, and reuse idempotency keys across attempts.
Backoff and jitter solve the problem of one client hitting one limit. They don't solve the problem of one application publishing to hundreds of connected social accounts across half a dozen platforms, each with its own counter, its own window, and its own definition of what "you" even means for rate-limiting purposes. At that scale the question stops being "how do I retry this request" and becomes "how do I schedule thousands of requests so the ones that are going to get throttled never get sent in the first place."
PostGate handles OAuth, publishing and delivery evidence for every network through one API. Start free or read the docs.
Are per-account and per-app limits separate?
Almost every platform layers at least two counters on top of each other: one scoped to your application as a whole, and one scoped to each individual connected account (or Page, or channel). X tracks per-user and per-app limits as independent counters — a single user maxing out their own posting cap doesn't block anyone else's, but the app-level ceiling sits above all of them combined — see X API Rate Limit. Meta runs the same shape with an X-App-Usage percentage at the app level and a separate X-Page-Usage percentage per Page — see Facebook API Rate Limit. At one connected account, this distinction barely matters. At two hundred connected accounts on the same app credentials, the app-level ceiling becomes the binding constraint long before any single account's own limit does — a burst of publishes spread evenly across 200 low-volume accounts can still exhaust the shared app budget.
Why does the fix move from retry logic to scheduling?
Reactive backoff — wait for a 429, then retry — is fine for occasional, unpredictable throttling. It doesn't scale to a known, predictable ceiling. If you know your app-level budget is roughly 200 × active users per hour on Meta, or a fixed request count per 15-minute window on X, the better strategy is to never send request number 201 in the first hour at all — queue it instead, and let a rate-aware dispatcher release requests at a pace the platform will actually accept. This shifts the problem from "handle rejection gracefully" to "a token-bucket or leaky-bucket limiter per platform, per app-credential, that gates outbound requests before they're sent."
class TokenBucket {
constructor(capacity, refillPerMs) {
this.capacity = capacity;
this.tokens = capacity;
this.refillPerMs = refillPerMs;
this.last = Date.now();
}
take() {
const now = Date.now();
this.tokens = Math.min(this.capacity, this.tokens + (now - this.last) * this.refillPerMs);
this.last = now;
if (this.tokens < 1) return false;
this.tokens -= 1;
return true;
}
}
// One bucket per platform (or per platform + app credential), sized to that
// platform's documented window, e.g. X: 100 posts / 15 min per user.
const buckets = {
x: new TokenBucket(100, 100 / (15 * 60 * 1000)),
facebook: new TokenBucket(200, 200 / (60 * 60 * 1000)),
};
function canSendNow(platform) {
return buckets[platform]?.take() ?? true;
}Why queue by destination, not by request?
At scale, a single global queue drained in submission order lets one noisy platform (or one account with a burst of scheduled posts firing at the same minute) starve everything else. Partition the queue by platform — and ideally by account within platform — so a throttled X target doesn't hold up a Facebook target that's ready to go. This is the same principle behind PostGate's own delivery model: a unified post fans out into independent per-target deliveries, each tracked through its own pending → publishing → published/failed lifecycle, so one target being rate-limited or in retry_scheduled never blocks the others — see Delivery lifecycle.
What does PostGate already absorb for you?
PostGate isolates provider credentials — and the quotas tied to them — per organization, so your own app-level rate ceiling on each platform is scoped to your workspace rather than shared across every PostGate customer (see the platform-app credentials covered in Platforms & quotas). Per-target delivery state, retry counts, and the next scheduled attempt are visible through delivery logs (GET /api/posts/{id}/logs), so you can see exactly which target is being throttled without maintaining that bookkeeping yourself. PostGate also retries provider delivery internally for transient platform failures — client-side effort at scale is better spent on your own submission pacing (the token-bucket approach above) than on duplicating retry logic PostGate already runs per target.
What actually changes past a handful of accounts?
- Rate limiting becomes a scheduling problem, not just a retry problem — gate requests before sending, don't only react after a 429.
- Track budget per platform and per app-credential, not globally — the app-level ceiling binds first once you have more than a few accounts.
- Partition queues by destination so one throttled platform or account never blocks delivery to the rest.
- Prefer webhooks over polling for status — a poll loop across hundreds of accounts burns the same budget you're trying to protect.
- Reuse an Idempotency-Key per logical post regardless of how many times your scheduler re-attempts it.
Frequently asked questions
What is the difference between per-account and per-app rate limits?
Almost every platform layers both: a counter scoped to each connected account and a ceiling scoped to your application as a whole. X tracks per-user and per-app limits independently — one user maxing out their cap doesn't block others, but the app-level ceiling sits above all of them combined. Meta exposes an X-App-Usage percentage at the app level and a separate X-Page-Usage per Page. With hundreds of accounts on one app credential, the app-level ceiling becomes the binding constraint first.
Should I wait for a 429 before slowing down my requests?
Reactive backoff is fine for occasional, unpredictable throttling, but it does not scale to a known, predictable ceiling. If you know your budget — roughly 200 requests per active user per hour on Meta, or a fixed count per 15-minute window on X — the better strategy is to never send the over-budget request at all: queue it, and let a token-bucket or leaky-bucket limiter per platform and app credential release requests at a pace the platform will actually accept.
How should I structure a publishing queue across many accounts?
Partition it by destination, not by submission order. A single global queue lets one noisy platform, or one account with a burst of scheduled posts, starve everything else. Partition by platform — and ideally by account within platform — so a throttled X target never holds up a Facebook target that is ready to go. Each per-target delivery should track its own lifecycle so one target being rate-limited never blocks the others.
What rate-limit handling does PostGate do for me?
PostGate isolates provider credentials — and the quotas tied to them — per organization, so your app-level ceiling on each platform is scoped to your workspace rather than shared across customers. Per-target delivery state, retry counts, and the next scheduled attempt are visible through delivery logs, and PostGate retries provider delivery internally for transient failures. Client-side effort at scale is better spent on your own submission pacing than duplicating that retry logic.
