When an API returns 429 Too Many Requests, stop sending and wait. Use the Retry-After value if the response includes one. If not, back off exponentially with random jitter. Limit concurrency on your side and queue work, so a rate limit turns into a short delay instead of a failed checkout or lost message.
What providers tell you
Two public examples show the pattern.
Slack's rate limit documentation says that if you exceed a limit on any of its HTTP APIs, including incoming webhooks, it returns a 429 and a Retry-After header with the number of seconds until you can retry. The limit applies to that method for that workspace. Calls to other methods for the same workspace, and calls to the same method for other workspaces, are not restricted.
A payment provider says limits are usually measured in requests per second per account, with some endpoints having their own limits and some concurrency limits. It tells you to watch for 429 responses, retry with exponential backoff, and add randomness to the schedule to avoid a thundering herd. Its 429 responses include a header explaining which limit was hit. It also suggests profiling request duration, since slow and expanded list requests use more resources and can trigger concurrency limiting.
What not to do
- Retry instantly in a tight loop. Every client that does this lengthens the problem for everyone.
- Retry without a cap. Set a maximum number of attempts and a maximum delay.
- Retry non idempotent writes without an idempotency key. A retry can duplicate the action.
- Ignore the header and use your own fixed delay, if the vendor says how long to wait.
A sound retry policy
- If Retry-After is present, wait at least that long, then retry.
- Otherwise wait a base delay, double it on each failure, and add random jitter.
- Cap the delay and the attempt count, then move the job to a dead letter queue.
- Use idempotency keys for any write.
- Track 429 counts as a metric and alert when they rise.
Design so limits rarely bite
| Technique | Why it helps |
|---|---|
| Queue outbound calls with a worker pool of fixed size | Caps concurrency |
| Batch where the API allows | Fewer requests for the same work |
| Paginate and filter by time | Smaller, cheaper requests |
| Cache stable lookups | Avoids repeat reads |
| Prefer webhooks over polling | Moves load to the provider |
Limits are also a security signal. A sudden spike in 429s can mean a runaway loop, a leaked key being used by someone else, or a traffic pumping attack on a messaging endpoint. Check your usage logs before simply raising limits.
Related reading
Retries can become the outage. See retry storms and webhook retry schedules, backoff and jitter.
Where Safeguard fits
Safeguard's dependency scanning covers the HTTP client and SDK libraries that implement your retry logic, and its policy gates can stop a vulnerable version from merging. A free scan is one way to check.
Quick check
What if there is no Retry-After header?
Use exponential backoff with jitter and cap both the delay and the number of attempts.
Should every request be retried after a 429?
Only if it is safe to repeat. For writes, send an idempotency key or reconcile first.
Sources
- Rate limits, Slack developer documentation
- Rate limits, Stripe documentation
Last reviewed: 19 October 2026.