API 429 Errors: Rate Limits, Retry-After, and Safe Retries

Distinguish rate limits from quota errors, add bounded retries, honor Retry-After, and avoid executing side effects twice.

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.

What 429 means

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.

Diagnostic table

SymptomWhat to checkResponse DataSafe Action
429 appeared after a surge in parallel requestsCompetitiveness and a short limit windowRetry-After, error fields, rate-limit headersReduce concurrency, wait for specified time, then send one probe
429 occurs on large requestsLimit of tokens for the periodUsage of previous queries, input size and max_tokensReduce packet or output, distribute load
429 returned on every requestQuota, balance, spend limit or project blockingMachine readable code and payment/limit messageStop retry and eliminate the cause in the provider's account
429 occurs only for some keys or projectsScope of the limitProject/org ID, key, current tierMatch the request to the correct project and its limits
After retry the number 429 growsRetry stormNumber of attempts, concurrency, queueStop the wave of replays, add jitter and overall time limit
The answer is actually 401 or 403Authentication or rightsHTTP status and error typeDo 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.

How to read Retry-After

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.

Limited retry with backoff and jitter

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.

What queries can be repeated

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:

  1. Server-supported idempotency key, created before the first attempt and reused without changes.
  2. Unique business identifier with existing result verification.
  3. Prohibition of automatic retry and manual confirmation of the actual state.

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.

How to confirm recovery

After the limit window ends, do not return all traffic at once.

  1. Submit one minimum controlled request.
  2. Make sure that the expected status and valid response body are received.
  3. Check usage and application log to ensure hidden replays do not continue.
  4. For an operation with a side effect, confirm that the object, payment or message is created exactly once.
  5. Increase the load step by step and monitor the share of 429 and the number of retry.

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 sequence

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.

Ready to optimize your LLM workflow?

Join thousands of developers building faster, smarter, and more cost-effective AI applications with BetterToken.