Default WorkspaceGuides
429 fallback, coding agents & gateway comparisons

OpenAI 429 Rate Limit Errors: Retry Correctly and Fall Back to Another Provider

Published · Facts and limits are dated; check the linked sources.

A 429 from the OpenAI API can mean two very different things, and the right response to each is opposite: one you should retry, the other you should never retry. This guide shows how to tell them apart, how to retry the temporary kind without making it worse, and how to fall back to another provider when waiting isn't an option. The fallback part uses SentioGate, which we build, so we have a stake; the OpenAI behavior below follows OpenAI's own documentation, linked in the sources.

As of 2026-10-26. Limits and error formats change, so check OpenAI's linked pages before relying on specifics.

The two kinds of 429

Look at error.code in the response body, not just the status:

error.codeMeaningWhat to do
rate_limit_exceededYou hit a requests-per-minute, tokens-per-minute or requests-per-day limitWait and retry. Honor Retry-After when present, otherwise back off exponentially with jitter.
insufficient_quotaYour organization is out of credits or at a spend limitDon't retry. It will keep failing. Add funds or raise the limit, or fail over to another provider.

Two things to know about the temporary kind:

  • Limits apply to your organization, not to a single key. Creating more API keys on the same organization doesn't raise them.
  • Token limits count your prompt and your requested output. A large max_tokens uses up the token budget even if the model replies briefly, so keep it close to what you need.

OpenAI responses include x-ratelimit-* headers (limit, remaining and reset values for requests and tokens), and a 429 carries a Retry-After header. Use them to pace traffic instead of guessing.

Retry the temporary kind correctly

Rules that work:

  1. Wait at least Retry-After if it is present.
  2. Otherwise use exponential backoff with random jitter, so many workers don't retry in the same instant.
  3. Cap the number of attempts and the total wait. Return an honest error rather than holding a request open for minutes.
  4. Never retry insufficient_quota.

Client-side retries help with brief traffic spikes, but when an organization hits a sustained rate limit or runs out of quota, retrying the same endpoint only exhausts limits faster. Handling cross-provider failover, shared cooldown state across processes, and multi-key fallback without stalling the caller is where a dedicated gateway is required.

When waiting isn't an option: fall back to another provider

For user-facing requests, a 30-second wait is often worse than an answer from a different model. A fallback needs three things:

  • Another provider you have access to, with its own key and its own limits.
  • State shared across requests, so after one 429 your other workers stop sending traffic to the throttled model instead of each discovering the limit themselves.
  • A decision about quality. A different model gives different answers. Decide which models are acceptable substitutes before an incident, not during one.

You can build this yourself with a small wrapper around two SDKs, but the shared cooldown state across processes is the harder part.

With SentioGate

SentioGate is an OpenAI-compatible, bring-your-own-key (BYOK) router. You add your provider keys (for example OpenAI plus Anthropic, Gemini or Groq) to its encrypted Vault, enable the models you consider acceptable, and send model: "auto". On a 429 it does the following:

  1. Puts that model on that key into a cooldown shared across requests, honoring the upstream Retry-After, with a short default when there isn't one.
  2. Tries your next key for the same model, then, with auto, the next-ranked model in your enabled pool, which can be on another provider. Models that are cooling down are skipped.
  3. A pinned provider/model never switches models. It only gets key failover, so you can keep a request on one model when you need it to.
  4. Fails fast if nothing is available: a 429 with code: "upstream_rate_limited" and a Retry-After header, instead of hanging.

auto only routes to models you enabled in the Vault, so you stay in control of which substitutes are allowed.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.sentiogate.dev/v1",
    api_key=os.environ["SENTIOGATE_API_KEY"],
)

resp = client.chat.completions.create(
    model="auto",  # ranks your enabled models; fails over on 429/5xx
    messages=[{"role": "user", "content": "Summarize this ticket in one line."}],
)
print(resp.choices[0].message.content)

The response includes router metadata (policy, reason, attempts), so you can log which provider and model actually answered. The SentioGate Python and JavaScript SDKs add optional retries that only wait on Retry-After; see the OpenRouter 429 guide for examples. As with the OpenAI SDK, use retries in one layer, not both.

What a fallback doesn't solve

  • Different models, different output. Test your prompts against every model you enable.
  • insufficient_quota on every provider still ends in an error. A fallback only helps if another provider has headroom.
  • Mid-stream failures. Errors that happen after streaming has begun can't be silently retried on another model without restarting the response.
  • SentioGate's own limits: it is hosted only, has no Anthropic Messages endpoint (/v1/messages) and no MCP gateway, and its observability is a per-request metadata and usage dashboard, not a tracing suite. See the gateway comparison for alternatives.

If you're weighing gateways more broadly, see the OpenRouter alternative, LiteLLM alternative and Portkey alternative guides, or the API docs.

Sources

OpenAI guidance accessed 2026-10-26; SentioGate behavior as implemented on that date.