AI Kit Guides
ChatGPT

OpenAI 429 Fix: How to Read Every Error Variation (2026)

OpenAI 429 isn't one error — it's several. Here's how to read the error body and headers, tell each 429 variation apart, and apply the right fix.

Peng Zhou Peng Zhou · · 1552 words
7 min read · 1552 words

For the longest time I treated every OpenAI 429 the same way: wrap the call in a try/except, add some backoff, move on. Then one afternoon I pasted an error into a chat room and someone replied, “bro that’s not a rate limit, that’s a quota error — your retry loop is making it worse.” I had been hammering an insufficient_quota 429 for twenty minutes, burning what little was left of the window, when the actual fix was a billing page I’d never opened.

That’s the trap with 429. It’s not one error. It’s a whole family of errors that all happen to share the same HTTP status code, and the difference between them lives in the response body — not in the number 429. If you can’t read that body, you’ll apply the wrong fix to every single one of them.

This guide is the reference I wish I’d had that afternoon: how to actually read an OpenAI 429, how to tell the variations apart, and the right fix for each.

Why “Just Add Retry” Fails

Most 429 advice online is a single sentence: “use exponential backoff.” It’s not wrong, but it’s incomplete in a way that costs you hours. Backoff is the correct fix for exactly one of the 429 variations. For the others, retrying is actively harmful — it extends your cooldown, fills your logs with noise, and delays the real fix.

The reason everyone reaches for retry first is that the HTTP layer only gives you 429. The useful information is one layer down, in the JSON error object and the response headers. Miss those and you’re guessing.

Step 1: Read the Body, Not the Status

Every OpenAI 429 comes with a body that looks roughly like this:

{
  "error": {
    "message": "Rate limit reached for gpt-5.6-turbo in organization org-xxx on tokens per min. Limit: 500000 / min. Current: 487234 / min.",
    "type": "rate_limit_exceeded",
    "code": "rate_limit_exceeded"
  }
}

The three fields that matter:

  • type — this is your branch key. rate_limit_exceeded is retryable; insufficient_quota is not.
  • code — often repeats the type, but can carry finer detail on newer models.
  • message — human-readable, and the most reliable place to catch a quota vs. rate-limit distinction.

Then read the headers on the response, not just on errors:

  • x-ratelimit-limit-requests / x-ratelimit-limit-tokens — your ceiling
  • x-ratelimit-remaining-requests / x-ratelimit-remaining-tokens — what’s left right now
  • x-ratelimit-reset-requests / x-ratelimit-reset-tokens — when the window resets (e.g. 6s, 1m30s)
  • retry-after-ms — the server’s own recommendation in milliseconds; honor it

The single most useful habit: log type, code, message, and x-ratelimit-remaining-* on every 429. One line of logging would have saved me that entire afternoon.

The Variations (and the Right Fix for Each)

Here are the 429 shapes I actually see in the wild, including the weird ones people paste into search bars.

Variation 1: rate_limit_exceeded (RPM or TPM)

The classic. The message tells you which dimension: “requests per min” vs “tokens per min.” If it’s TPM, you’re sending long prompts or a fat system prompt multiplied across many calls.

Fix: exponential backoff with jitter, and respect retry-after-ms. I won’t re-paste the full code here — my ChatGPT 429 fixes guide has the exact Python I run in production, including the jitter detail that stops ten threads from retrying in lockstep. The short version: wait a random-ish increasing interval, cap the attempts, and surface the failure instead of looping forever.

If you’re hitting TPM a lot, trimming your system prompt and switching batch jobs to the Batch API (half price, far higher limits) eliminates most of it.

Variation 2: insufficient_quota / “current quota”

This is the one that fooled me. The message says things like “You exceeded your current quota” or references current quota. It is not a rate limit — it means your organization’s spend cap, monthly budget, or account state is blocking you.

Fix: stop retrying. No amount of backoff restores quota. Open Billing, Usage, and Limits for the same organization and project the call came from. Check the monthly spend cap, the model’s access for that project, and whether a different org is eating the budget. Creating a new API key will not help — keys share the org’s pool, not the other way around.

Variation 3: request_retry:68 / 429001 in the logs

You’ll see these when people copy their raw error text into a search engine — sources.request_retry or request_retry:68 openai 429 or 429001. These are OpenAI’s internal retry metadata, not the public type. A 429 carrying this context usually means the model snapshot is cooling down or you’re pressing a concurrency cap on one specific model.

Fix: this is a short-term, model-specific pressure signal. Back off, watch x-ratelimit-remaining-*, and don’t fan out more parallel calls to that same model. If you can, route the traffic to a different model or a smaller variant until the window clears.

Variation 4: “all credentials for model … are cooling down”

A message like error: http 429: all credentials for model gpt-5.6-terra are cooling down is a model-specific cooldown. It shows up after a burst or a capacity event on that particular snapshot.

Fix: wait it out with backoff, or shift that traffic to another model. It’s temporary — but retrying every second just keeps you pinned at the top of the cooldown.

Variation 5: “method not allowed” on a 429

detail: "method not allowed" paired with a 429 is a trick one. It’s usually not a rate limit at all — it means the endpoint or HTTP method is wrong (POST where it wanted GET, or a path that doesn’t exist for that model).

Fix: fix the request. Don’t add backoff. Retrying a malformed call just wastes your limit window and teaches you nothing.

Variation 6: max_check_attempts / agent loop limits

If you’re running an agent (Claude Code, Codex, or your own loop), you may hit max_check_attempts or similar — the model burned its tool-call or reasoning budget. This is a loop-design problem, not an API quota problem.

Fix: cap the loop’s iterations and add a stop condition. I wrote about this in my Loop Engineering piece — a loop with no stop condition is just an expensive while(true). Give the agent a max attempts and a verification step, and fail gracefully instead of spinning.

A Diagnostic Function You Can Drop In

Instead of guessing, route on the body. Here’s the shape of the logic I use — read the type, branch, and only retry when the evidence says “short-term pressure”:

def classify_429(error_body: dict, headers: dict) -> str:
    etype = error_body.get("error", {}).get("type", "")
    message = error_body.get("error", {}).get("message", "").lower()

    if etype == "insufficient_quota" or "current quota" in message:
        return "QUOTA"          # stop. check billing/tier
    if etype == "rate_limit_exceeded":
        return "RATE_LIMIT"      # backoff + respect retry-after-ms
    if "cooling down" in message:
        return "MODEL_COOLDOWN"  # wait or route to another model
    if "method not allowed" in message:
        return "BAD_REQUEST"     # fix the call, don't retry
    return "UNKNOWN"             # collect evidence, don't guess

The point isn’t the exact code — it’s that you decide the branch from the body, not from the number 429. Once you do that, the right fix for each variation becomes obvious, and you stop wasting retries on problems retries can’t solve.

When Retrying Is the Wrong Move

Here’s the rule I now live by: retry only when (a) the type is rate_limit_exceeded, and (b) the headers show a reset signal. If the body points at quota, billing, a wrong model, a wrong method, or a platform event, the same request fired again is not a fix — it’s noise. Collect the error body, the request id, the headers, your project/org, and the model, then go fix the actual cause.

Also worth knowing: failed requests still count against your per-minute limit. A tight retry loop can turn one 429 into fifty. Backoff that lowers concurrency, queues the work, or shrinks the request is what actually helps — not hammering the reset window harder.

Tying It Back to the Limits Themselves

If you want the theory behind why these limits exist and how the tiers map to RPM/TPM, my OpenAI rate limits guide breaks down the two 429 types, TPM vs RPM, and the five API tiers. Read that to understand the budget; read this to decode the error when the budget runs out. They’re two halves of the same problem.

And if long prompts are what’s eating your TPM, my prompt engineering techniques guide has concrete ways to cut tokens without losing quality — often the cheapest fix for a TPM 429.

Summary

A 429 is not a verdict; it’s a category. The number tells you almost nothing. The type, the code, the message, and the x-ratelimit-* headers tell you everything. Read them, branch on them, and apply the matching fix:

  • rate_limit_exceeded → backoff + respect retry-after
  • insufficient_quota → stop, fix billing/tier
  • cooling down / request_retry → wait or route elsewhere
  • method not allowed → fix the call
  • agent loop limits → cap the loop

Do that, and the next time a 429 shows up in your logs, you’ll spend thirty seconds fixing it instead of thirty minutes guessing at it — like I used to.

Frequently Asked Questions

Is every OpenAI 429 retryable?

No. Only the rate_limit_exceeded type (RPM/TPM pressure with reset headers) should be retried with backoff. If the body says insufficient_quota or current quota, retrying is pointless — the fix is in your billing or usage tier, not in more attempts.

What does request_retry:68 or 429001 mean?

Those appear in OpenAI's internal retry metadata, not as the public error type. A 429 with that context usually means the model is cooling down or you are hitting a concurrency cap on a specific model snapshot. Back off, check x-ratelimit headers, and avoid hammering the same model.

Why do I get a 429 that says 'method not allowed'?

That is not really a rate limit. It means the endpoint or HTTP method you called is wrong (e.g. POST vs GET, or a path that doesn't exist for that model). Fix the request, don't add backoff — retrying the same bad call just wastes your limit window.

My error says 'all credentials are cooling down' — what now?

A specific model snapshot is temporarily throttled, often after a burst or a capacity event. This is a short-term, model-specific cooldown. Wait it out with backoff, or route that traffic to a different model or a smaller variant until the window clears.

Peng Zhou

Peng Zhou

AI Kit Guides is dedicated to helping you navigate the world of AI tools with honest, practical, and beginner-friendly content.

Tags:

#openai 429 #error 429 #rate limit #insufficient_quota #api error fix #python
Share:

Related Articles

ChatGPT

ChatGPT for Beginners: Start Here in 2026

New to ChatGPT? This beginner-friendly guide covers everything from creating an account to writing your first prompt, with practical examples and tips.

Stay Updated on AI Tools

Get the latest AI tool reviews, tutorials, and comparison guides delivered to your RSS reader.

Subscribe via RSS