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
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.
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:
- Server-supported idempotency key, created before the first attempt and reused without changes.
- Unique business identifier with existing result verification.
- 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.
- Submit one minimum controlled request.
- Make sure that the expected status and valid response body are received.
- Check usage and application log to ensure hidden replays do not continue.
- For an operation with a side effect, confirm that the object, payment or message is created exactly once.
- 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.