Introduction
When integrating with our APIs, clients may occasionally receive an HTTP 429 – Too Many Requests response.
This indicates that the request rate from the client has temporarily exceeded the allowed limits.
Our rate-limiting implementation is designed to protect system stability and ensure fair usage across all clients.
This article describes how the rate limiter works and how client applications should safely handle 429 responses.
Rate limiting model (token bucket)
Rate limits are applied per identity:
- Authenticated requests → per user ID
- Unauthenticated requests → per IP address
Each identity has a token bucket:
- Each request consumes one token
- Tokens refill at a fixed tokens-per-second rate
- When no tokens remain → API responds with HTTP 429
- Once tokens accumulate again → requests succeed normally
Handling 429 responses: exponential backoff with jitter
Clients must implement exponential backoff with jitter since the API does not specify how long to wait before retrying.
Recommended backoff schedule
| Retry attempt | Delay |
| 1 | 500–1000 ms |
| 2 | 1–2 s |
| 3 | 2–4 s |
| 4 | 4–8 s |
| 5 | 8–16 s |
Why jitter?
Without randomness, multiple clients retry at the same time → causing synchronized bursts that may generate more 429s and destabilize the system.
Jitter spreads retry attempts over time and stabilizes recovery
Maximum retry cap
To avoid infinite retry loops or cascading failures:
- Max delay: 30–60 seconds
- Max attempts: 5–7
- After exceeding the limit → stop retrying and return an error upstream.
What not to do
Clients should adhere to these guidelines:
❌ Do not retry immediately after a 429
❌ Do not retry requests in parallel
❌ Do not spike request volume after recovery
❌ Do not assume any fixed rate window or quota
(e.g., “we can do 10 requests/sec” or “limit resets at :00”)
Pseudocode example
Below is a generic pseudocode example illustrating a way to implement the exponential backoff with jitter.
Basic exponential backoff with jitter
function sendWithRetry(request):
baseDelay = 500 // ms
maxDelay = 60000 // ms
maxAttempts = 7
attempt = 0
while attempt < maxAttempts:
response = sendHttpRequest(request)
if response.status != 429:
return response
attempt += 1
// exponential delay
delay = baseDelay * (2 ^ (attempt - 1))
// jitter ±50%
jitter = randomBetween(0.5, 1.5)
delay = delay * jitter
delay = min(delay, maxDelay)
sleep(delay)
throw "Max retry attempts exceeded"What the rate limiter does not provide
Our current rate-limiting implementation does not return:
- Retry-After header
- Remaining quota for the current window
- Information about the size or boundaries of the time window
Clients must implement their own backoff logic and treat every 429 as a signal to temporarily reduce the request rate.
Summary
- An HTTP 429 indicates that the request rate has exceeded the allowed limits.
- The server does not indicate how long to wait before retrying.
- Clients must implement exponential backoff with jitter, with strict retry caps.
- Avoid parallel retries and assumptions about rate windows.
- The pseudocode above illustrates correct and safe retry behavior.