An API 429 error means the server rejected a request because of a rate or usage limit. Read the response body and headers first, identify the limit, and allow only a bounded retry. For operations with side effects, determine whether the first request completed before sending another one.
RFC 6585 defines 429 as "too many requests in some period." The response may contain Retry-After
, but the standard does not specify a single way of counting: the server can limit a single resource, a key, a project, an organization, or an entire cluster.
Therefore, status 429 in itself does not answer the question “how long to wait.” For one API, this is a short-term RPM or TPM limit; for another, it is an exhausted balance, a monthly quota, or a set spending limit. For example, in the current OpenAI error table different reasons 429 have different codes: temporary exceeding the rate limit, exhausted credit and project or organization limits. They cannot be repeated by one handler.
| Symptom | What to check | Response Data | Safe Action |
|---|---|---|---|
| 429 appeared after a surge in parallel requests | Competitiveness and a short limit window | ||
Retry-After , error fields, rate-limit headers |
|||
| Reduce concurrency, wait for specified time, then send one probe | |||
| 429 occurs on large requests | Limit of tokens for the period | Usage of previous queries, input size and max_tokens |
|
| Reduce packet or output, distribute load | |||
| 429 returned on every request | Quota, balance, spend limit or project blocking | Machine readable code and payment/limit message |
|
| Stop retry and eliminate the cause in the provider's account | |||
| 429 occurs only for some keys or projects | Scope of the limit | Project/org ID, key, current tier | Match the request to the correct project and its limits |
| After retry the number 429 grows | Retry storm | Number of attempts, concurrency, queue | Stop the wave of replays, add jitter and overall time limit |
| The answer is actually 401 or 403 | Authentication or rights | HTTP status and error type | Do not repeat as 429; fix key, rights or regional restriction |
If 429 occurs in a request via BetterToken, current API documentation and Dashboard provide an additional verification point: there you can compare the time, model, status, input/output/cache tokens and consumption of your own request. This data helps restore the load profile, but does not prove the cause of 429 with another provider; for this we need his answer and documentation.
First save the full HTTP status, error body and headers without the API Key. If a valid Retry-After
is present, consider it the minimum delay. Add a small random jitter to prevent multiple workers from waking up at the same time.
If there is no header or it is not parsed, use an exponential delay with jitter and a hard limit on the number of attempts and total time. OpenAI recommendations on rate limits specifically warn that unsuccessful requests can also be counted towards the minute limit: frequent repetitions can only prolong the problem.
Do not transfer specific header names between providers. First, check the current contract of the required API: the set of fields, time units and the scope of the limit may differ.
The following is pseudocode, not a finished library. Choose delays and attempt limits for the specific operation and the provider's documentation.
function send_with_retry(request, max_attempts, total_deadline):
attempt = 0
started_at = now()
while attempt < max_attempts and now() < total_deadline:
response = send(request)
if response.status != 429:
return response
error = parse_error(response.body)
if error means quota, balance, spend limit or manual action:
stop without retry
if response has valid Retry-After:
base_wait = parse_retry_after(response)
else:
base_wait = exponential_delay(attempt)
sleep(base_wait + random_jitter())
attempt += 1
raise RetryBudgetExhausted
Before adding your own loop, check if the SDK does automatic retries. Otherwise, one “application replay” can turn into several hidden HTTP requests.
Repeating a status or list read is usually safer because it doesn't have to create a new object. But POST
, which sends a message, debits money, starts a task, or creates a record, may be executed before the client receives a response.
This operation requires one of three mechanisms:
You can’t add an idempotency key “for show”: first check that a specific endpoint actually supports it and how long it stores the result.
After the limit window ends, do not return all traffic at once.
If the probe gets a 429 again with a quota or balance message, waiting won't solve the problem. If 429 only appears when concurrency increases, limit queuing and concurrency rather than increasing retries.
The safe order is: save the response → define type 429 → perform the required action or withstand Retry-After
→ use limited backoff with jitter → test one request → eliminate duplicate side effect. Infinite retry and a generic handler for 401, 403, 429 and timeout hide the reason and create a new load.
Originally published on the BetterToken blog.
BetterToken provides pay-as-you-go access to AI model APIs through
OpenAI-compatible and Anthropic-compatible endpoints — useful if you are wiring
Claude Code, Codex, or your own tooling to a custom base URL.
See the docs to get started.