In this categoryTroubleshooting ยท 27
TroubleshootingIntermediate

How to Fix OpenAI 429 Rate Limit Errors With Backoff

Stop your app from crashing on 429s by adding exponential backoff and reading the retry headers.

9 minIntermediate

A 429 means you sent requests faster than your tier allows, or you ran out of tokens-per-minute headroom. The wrong reaction is to retry instantly in a tight loop, which makes it worse. The right fix is to back off, respect the retry hint the API gives you, and spread requests over time.

  • A working OpenAI API key
  • Node 18+ or Python 3.9+
  • The ability to read response headers from your HTTP client

Step 1: Tell apart the two kinds of 429

There are two distinct causes. A rate limit means too many requests or tokens in a short window, and it clears on its own. An insufficient_quota error also returns 429 but means your account is out of credits, and no amount of retrying helps. Read the code field to know which one you have.

zsh - api
$curl -i https://api.openai.com/v1/chat/completions ...
HTTP/2 429
retry-after: 2
x-ratelimit-remaining-requests: 0
{ "error": { "code": "rate_limit_exceeded" } }
$
Quota is not a rate limit
If the code is insufficient_quota, add credits or a payment method. Backoff will never clear it.

Step 2: Read the retry-after and ratelimit headers

The API ships headers that tell you exactly how long to wait and how much budget remains. Honor retry-after when present instead of guessing a delay.

Response headers
x-ratelimit-limit-requests: 500
x-ratelimit-remaining-requests: 0
x-ratelimit-reset-requests: 1.2s
x-ratelimit-remaining-tokens: 9540
retry-after: 2
When remaining drops to 0, wait for the reset window before sending again.

Step 3: Add exponential backoff with jitter

Wrap your call in a retry loop that doubles the delay each attempt and adds a small random jitter so many clients do not retry in lockstep. Cap the number of attempts so a real outage does not hang forever.

retry.js
async function withBackoff(fn, max = 5) {
  for (let attempt = 0; attempt < max; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (err.status !== 429 || attempt === max - 1) throw err;
      const retryAfter = Number(err.headers?.["retry-after"]) || 0;
      const wait = retryAfter * 1000 || (2 ** attempt) * 500 + Math.random() * 250;
      await new Promise((r) => setTimeout(r, wait));
    }
  }
}

Step 4: Lower your request pressure

Backoff treats the symptom. To stop hitting the limit at all, batch where you can, use a smaller model for cheap calls, and add a concurrency limit so you never have more than a handful of in-flight requests at once.

Watch tokens, not just requests
Most surprise 429s come from the tokens-per-minute limit, not requests-per-minute. Long prompts burn the token budget fast even at a low request count.

Result

With backoff plus a concurrency cap of 5, a batch job that previously failed on roughly one in ten calls now finishes cleanly. The job is a little slower during peak windows but never drops a request.

Watch related tutorials

Free weekly email

New guides in your inbox

Fresh step-by-step how-to guides as we publish them. One email a week, no more.

Tags
#openai#rate limit#429#backoff