General interview: How should HTTP 429 and Retry-After guide client retries?
Prompt and context
An API returns 429 Too Many Requests after a traffic spike. Explain the status code's responsibility boundary, how to read Retry-After, which requests can be retried, and how to prevent the client from turning overload into a cascading failure.
What the interviewer evaluates
- Understanding 429 as a rate-limit signal rather than a permanent server failure.
- Correctly handling both Retry-After formats and a missing header.
- Combining idempotency, deadlines, and business side effects when deciding to retry.
- Protecting the service with exponential backoff, jitter, budgets, and rate control.
Questions to clarify before answering
- Is the limit per user, token, tenant, IP, or shared resource?
- Does the response contain Retry-After, and can the client trust its clock?
- Is the method and business operation idempotent, or is there an idempotency key?
- Are there an overall deadline, retry cap, and retry budget?
- Does the service expose quota, remaining capacity, or a request ID?
30-second answer framework
I treat 429 as rate-limit feedback and read Retry-After, which can be seconds or an HTTP date. I honor the server advice within a safe client backoff bound and add jitter. I automatically retry only idempotent operations or operations protected by an idempotency key, with deadline, attempt, and budget limits. Without Retry-After, I use jittered exponential backoff; repeated 429s stop and return control to the caller.
Step-by-step deep dive
Step 1: Confirm the 429 semantics
RFC 6585 defines 429 for too many requests in a given period and allows Retry-After. It tells the client to reduce rate; it should not be treated like a 500 and retried immediately.
Step 2: Parse Retry-After
Retry-After can be non-negative integer seconds or an HTTP date. For a date, use the response Date or a trusted clock to estimate the delay, and bound negative, huge, or malformed values.
Retry-After: 8
Retry-After: Wed, 02 Aug 2026 02:00:00 GMTStep 3: Decide whether replay is safe
Safe methods such as GET and HEAD are usually retryable. Writes require idempotent semantics, an idempotency key, or server-side deduplication. Even the same method can have side effects such as charging, sending mail, or creating a task.
Step 4: Compute the delay and backoff
Honor Retry-After first, then apply a client exponential-backoff ceiling and random jitter. Jitter prevents many clients from waking at the same instant. The wait must fit the request deadline rather than extending it forever.
Step 5: Limit retry amplification
Set per-request attempts, a global retry budget, and a concurrency cap. Reduce the rate for a resource that keeps returning 429; use a local queue or circuit breaker when needed so retries do not consume normal traffic capacity.
Step 6: Separate client and server responsibilities
The server provides an explicit limit signal and observable fields. The client respects the signal, backs off, and stops. After recovery, ramp traffic gradually instead of releasing every waiting request together.
Step 7: Record results and improve
Track the 429 rate, Retry-After distribution, final success rate, retry count, and deadline abandonments. Use the data to tune quotas, client budgets, and alerts rather than treating 429 as a one-request failure.
High-quality sample answer
I would first confirm the limit dimension and response headers. With Retry-After: 8, the client waits at least eight seconds; with an HTTP date, it computes the delay using a trusted clock and a maximum bound. An order lookup with an idempotency key can retry automatically, while a charge creation without deduplication must be confirmed by the business layer. I use exponential backoff with random jitter, a three-attempt per-request cap, a global retry budget, and an overall deadline. Persistent 429s lower concurrency and pause the queue so retries do not amplify overload. Metrics cover the 429 rate, wait time, and final success rate for quota and alert tuning.
Common mistakes
- Treating 429 like 500 and retrying immediately in a tight loop.
- Supporting only integer Retry-After and ignoring the HTTP-date form.
- Failing to distinguish idempotent reads from side-effecting writes.
- Omitting deadlines, budgets, or concurrency limits so retries grow without bound.
- Giving every client the same fixed delay and creating a synchronized retry spike.
Follow-ups and responses
Follow-up 1: How long should you wait without Retry-After?
Use jittered exponential backoff with maximum delay, attempt, and total-deadline limits. Tune it from the 429 rate and quota rather than choosing one permanent constant.
Follow-up 2: What if the HTTP date is in the past?
Treat the server delay as zero but still apply local backoff and jitter, and record a clock or server-generation issue. Do not launch high-concurrency retries because the date is invalid.
Follow-up 3: Can POST be retried?
Only when the API defines idempotency, provides an idempotency key, or the business layer can deduplicate. Otherwise return to the caller for an explicit resubmission decision.
Follow-up 4: What if many instances share one quota?
Coordinate retry budgets and rate control across processes or at tenant scope, using shared signals to reduce concurrency. Per-instance backoff cannot prevent aggregate overage.
Follow-up 5: How do you avoid another overload spike during recovery?
Ramp the queue gradually, keep jitter and a concurrency cap, and increase the rate only after observing 429s and latency. Do not release all waiting requests at once.
Follow-up 6: Which metrics prove the strategy works?
Compare 429 rate, retry amplification, success rate, P95 latency, final abandonment, and recovery time, segmented by tenant or resource.