Errors & blockers

Handling a 429 rate limit from a social API

How to read a 429 response correctly and back off without making the underlying rate limit worse.

Quick answer

A 429 means the platform understood your request but you exceeded a counter it tracks — it needs time, not a fix. Read the response headers (Retry-After, X's x-rate-limit-reset, Meta's X-App-Usage) to learn which limit you hit and when it resets, then wait that long, retrying with jitter, a retry cap, and the same idempotency key.

A 429 is not the API saying "something broke." It is the API saying "I know exactly what you asked for, and you asked too fast." That distinction matters, because the two failure modes call for opposite responses: a broken request needs a fix, but a throttled request needs nothing except time. Treat a 429 like a 5xx — retry immediately, maybe in parallel across a few workers — and you don't recover faster. You extend the same limiter's window and make the next request more likely to be throttled too. This post covers how to read a 429 correctly, across any social platform, before you write a single line of retry logic.

PostGate handles OAuth, publishing and delivery evidence for every network through one API. Start free or read the docs.

01

What does a 429 actually tell you?

Every major social API returns 429 Too Many Requests for the same underlying reason: you exceeded a counter the platform is tracking against your app, your user token, or both, inside a fixed or rolling time window. The status code alone tells you almost nothing about which counter you hit or when it resets — for that, you need the response headers, and every platform names them differently. X ships x-rate-limit-limit, x-rate-limit-remaining, and x-rate-limit-reset (a Unix timestamp) on every response, win or lose — see X API Rate Limit. Meta reports usage as percentages on X-App-Usage and X-Page-Usage rather than a hard remaining count — see Facebook API Rate Limit. Reading the headers on the request that just failed, not guessing from the status code alone, is the first thing a correct client does.

02

Why is Retry-After a contract, not a suggestion?

When a platform sends Retry-After (in seconds, or as an HTTP date) or a reset timestamp, that value is the platform telling you exactly how long its own limiter needs before it will accept another request from you. Ignoring it and retrying on your own fixed schedule — say, every 2 seconds regardless of what the header said — means you'll keep hitting the same wall until your guess happens to overshoot the real reset. Respecting it means your very next attempt after the wait has a real chance of succeeding instead of adding one more rejected request to the count some platforms use to decide whether to extend a penalty window.

Reading the header correctly (Node.js)PostGate API
async function requestWithRateLimitAwareness(url, options) {
  const res = await fetch(url, options);
  if (res.status === 429) {
    const retryAfter = res.headers.get('retry-after');
    const resetHeader = res.headers.get('x-rate-limit-reset'); // platform-specific name
    let waitMs;
    if (retryAfter) {
      // Retry-After is seconds, or occasionally an HTTP date
      const seconds = Number(retryAfter);
      waitMs = Number.isNaN(seconds) ? new Date(retryAfter).getTime() - Date.now() : seconds * 1000;
    } else if (resetHeader) {
      waitMs = Number(resetHeader) * 1000 - Date.now();
    } else {
      waitMs = 30_000; // no header at all — fall back to a conservative default
    }
    throw { retryable: true, waitMs: Math.max(waitMs, 0) };
  }
  return res;
}
03

Does every 429 mean the same thing?

A request-count throttle and a billing or included-volume cap can both surface as a 429 or a 429-equivalent error on some platforms, but only one of them is fixed by waiting — the other needs a plan upgrade or a purchased quota increase, and no amount of backoff will change that. X, for example, runs a per-window rate limit and a separate monthly billing cap as two independent systems — see X API Rate Limit. YouTube's Data API doesn't throttle by request count at all; it debits a points budget per operation and returns 403 quotaExceeded when the budget is gone for the day — see YouTube API 403 quota exceeded. Before you write retry logic, confirm which kind of limit you actually hit; retrying against a billing cap just burns your own compute waiting for a reset that isn't coming until the next billing cycle.

04

PostGate normalizes the signal for you

PostGate sits between your integration and each platform's own throttling, and it collapses all of that platform-specific header parsing into one structured signal: an error.code of rate_limited (or platform_transient for a broader transient failure) with retryable: true in the error envelope. Your client doesn't need to know whether the underlying rejection came from X's x-rate-limit-reset header or Meta's X-App-Usage percentage — it needs to check one boolean field. See Errors & retries for the full envelope shape.

A rate-limited target, normalizedPostGate API
{
  "error": {
    "code": "rate_limited",
    "message": "Instagram is rate-limiting this account.",
    "platform": "instagram",
    "retryable": true,
    "details": {},
    "request_id": "req_01JXYZ"
  }
}
05

How do you back off without making it worse?

The single most common way teams make a rate limit worse is retrying in a tight loop from multiple workers at once — a thundering herd that all wakes up at the same reset timestamp and gets throttled again together. Add jitter to whatever wait time the header gives you, cap how many times you'll retry a single logical request, and reuse the same Idempotency-Key across attempts so a retried request never becomes a second post. The mechanics of jitter and backoff scheduling are covered in Exponential backoff for social API retries.

Frequently asked questions

Should I obey the Retry-After header on a 429 response?

Yes — it is the platform telling you exactly how long its own limiter needs before it will accept another request from you. Retrying on your own fixed schedule means hitting the same wall until your guess overshoots the real reset, and some platforms count those rejected requests when deciding whether to extend a penalty window. Respecting it gives your next attempt a real chance of succeeding.

Is every 429 fixed by waiting?

No. A request-count throttle and a billing or included-volume cap can both surface as a 429 or equivalent, but only the throttle is fixed by waiting — the cap needs a plan upgrade or purchased quota. X runs a per-window rate limit and a monthly billing cap as two independent systems, and YouTube debits a points budget instead, returning 403 quotaExceeded when the day's budget is gone.

How do I retry after a rate limit without making it worse?

Avoid the thundering herd: multiple workers retrying in a tight loop all wake at the same reset timestamp and get throttled again together. Add jitter to whatever wait time the header gives you, cap how many times you retry a single logical request, and reuse the same Idempotency-Key across attempts so a retried request never becomes a second post.

How does PostGate report platform rate limits to my code?

PostGate collapses each platform's header parsing into one structured signal: an error.code of rate_limited (or platform_transient for a broader transient failure) with retryable: true in the error envelope. Your client checks one boolean field instead of knowing whether the rejection came from X's x-rate-limit-reset header or Meta's X-App-Usage percentage.

Build it with PostGate

One API for every social platform your customers care about — sandboxed, documented, and ready today.

Start building Read the docs