Use when calling HTTP APIs from Python to retry 429 responses with exponential backoff instead of crashing on the first rate-limit.
HTTP APIs return 429 when the caller is rate-limited. Code that
ignores the status and calls raise_for_status() immediately crashes
the whole script on the first throttle — even though a second attempt
a few seconds later would almost always succeed:
requests.exceptions.HTTPError: 429 Client Error: Too Many Requests
Wrong — happy-path only; dies on the first 429:
def fetch_user(client, user_id):
resp = client.get(f"/users/{user_id}")
resp.raise_for_status()
return resp.json()
Right — bounded retry with exponential backoff on 429:
import time
def fetch_user(client, user_id, max_attempts=4):
resp = None
for attempt in range(max_attempts):
resp = client.get(f"/users/{user_id}")
if resp.status_code != 429:
break
time.sleep(2 ** attempt)
resp.raise_for_status()
return resp.json()
If the server returns a Retry-After header (seconds or HTTP date),
honor it instead of your own schedule:
wait = int(resp.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
Never let a backoff loop sleep indefinitely. A bounded attempt count (3–5) plus a ceiling on the delay (e.g. 30 s) keeps a slow upstream from freezing the caller's process forever.
requests.adapters.HTTPAdapter(max_retries=...), urllib3.Retry,
and httpx.Client(transport=...) all support retry policies that
cover 429 plus 5xx correctly. Reach for those before rolling your
own loop — they handle jitter, connection failures, and Retry-After
out of the box.