On this page
How to Add Retry Logic to API Calls
Add retries to flaky API calls the right way: which errors to retry, exponential backoff with jitter, and how to test that it works.
Quick answer
- Retry only transient failures: network errors, timeouts, 429, and 5xx responses.
- Use exponential backoff with jitter and a small max-attempts cap.
- Prefer a library (axios-retry, tenacity) over hand-rolled loops so the details are battle-tested.
Which errors should I retry?
Retry network-level failures (DNS, connection refused, timeout) and responses that signal the server was transiently unable: 429 Too Many Requests, 502, 503, 504. Never retry 4xx client errors like 400, 401, 403, or 422 — the request itself is wrong and retrying it just wastes quota and adds load. Many HTTP clients expose a retry policy; configuring it beats writing your own loop.
How do I add retries with a library?
Pick the library for your stack: axios-retry for Node, tenacity for Python, or the built-in retry in AWS SDKs. Configure max attempts, backoff, and which errors to retry. The Python example retries up to five times with exponential backoff and jitter on transient failures only.
import requests
from tenacity import retry, stop_after_attempt, wait_exponential_jitter, retry_if_exception_type
from requests.exceptions import ConnectionError, Timeout
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential_jitter(initial=1, max=30),
retry=retry_if_exception_type((ConnectionError, Timeout)),
)
def fetch(url):
return requests.get(url, timeout=10)How do I know the retries actually work?
Test against a stub that fails the first two times and succeeds on the third, then assert the caller got a success and saw the expected delay pattern. For HTTP status retries, point the client at a local server returning 503 twice. This is easy to unit test and worth doing — untested retry logic usually has the fatal bug of retrying on the wrong errors.
# Test: server returns 503 twice, then 200
# assert fetch("http://localhost:9000") returns 200
# assert the server saw 3 requestsWhere this bites vibecoders
AI assistants make two opposite mistakes here: either no retry at all (a transient 503 permanently fails a job) or a naive while True retry loop with no cap that hammers the API until the platform kills the process. Libraries with sane defaults close both gaps. The detail assistants also skip: your retried call must be idempotent, or ‘retry once’ becomes ‘charge the customer twice’.
Where AI coding assistants get this wrong
- Retrying on 401/403, which can’t succeed and may lock accounts with repeated attempts.
- No cap on attempts, so a retry loop runs until timeout or process death.
- No jitter, so a fleet of retrying clients hammers the API in sync.
- Retrying non-idempotent POSTs without an idempotency key.
Checklist
- Retry only transient failures — never 4xx client errors.
- Use a maintained retry library with backoff, jitter, and a cap.
- Set timeouts on the underlying request so retries don’t hang.
- Unit-test that a flaky endpoint is handled gracefully.
FAQ
How many retries is the right number?
Three to five attempts is typical for API calls. More retries only help if the failure is transient and slow to clear; beyond a few attempts you should surface the error to the user or a dead letter queue rather than keep trying.
Should I retry when the server sends 429 Too Many Requests?
Yes, but honor the server’s Retry-After header when present, and don’t retry so eagerly that you make the rate limit worse. Back off longer than the rate-limit window and consider jittering the initial request timing so you’re not synchronized with other clients.
Related topics
- What Is Exponential Backoff?
- What Is Idempotency (and Why Does It Matter for APIs)?
- What Is a Dead Letter Queue?
- What Is Rate Limiting?
- What Is a Webhook?
- What Is the Circuit Breaker Pattern?