AlmaLinux vs CentOS: The New Stable Alternati
With Red Hat discontinuing CentOS in favor of CentOS St...




A rate limiter that returns 429 is doing its job, and a rate limiter that returns 503 is hiding a different failure behind a lie of a status code — the first describes a client that asked too often, the second describes a server that could not answer, and confusing them sends your retry logic after the wrong fix.
That redefinition is what makes the choice tractable. A 429 Too Many Requests is a contract: the service is healthy, your request is valid, you have simply exhausted a quota, and the Retry-After header tells you exactly when asking again will succeed. A 503 Service Unavailable is a confession: something on the server side — capacity, dependencies, a health check, a maintenance window — is not available right now, and the same Retry-After header carries far less certainty. RFC 6585 defined 429 in 2012 precisely because the 5xx series had no honest way to say "the problem is your pace, not our health." As of October 2026, the major API ecosystems have converged on this split: GitHub's REST API returns 429 for secondary rate limits with a Retry-After header measured in seconds, and the large model APIs — OpenAI, Anthropic, Google Gemini — all signal quota exhaustion with 429 while reserving 503 for genuine backend overload, per their current developer documentation.
What follows is the decision framework for emitting and interpreting each code: what RFC 9110 actually says about them, how to read the five signals that disambiguate a production response, which code your own rate limiter should emit for each of six implementation states, the client-side retry behaviour each code deserves, and the mistakes — Retry-After omissions, thundering herds behind a shared 503, SLO dashboards that count 429s as outages — that cause the most incident time. A comparison table separates 429 from 503 and from their nearest neighbours 502, 504 and 500, because half of all wrong fixes start as wrong status-code reads.
The direct answer: 429 means the client has sent too many requests in a given time window and should slow down or stop, while 503 means the server is temporarily unable to handle the request due to overload or maintenance — the first is a client-error class 4xx, the second a server-error class 5xx, and that classification alone determines who is responsible for the condition.
The definitions are short but load-bearing. 429, defined in RFC 6585 and carried into modern HTTP semantics, states that the user has sent too many requests in a given amount of time, and the response SHOULD include a Retry-After header indicating how long to wait. 503, defined in RFC 9110 Section 15.6.4, states that the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, and it too SHOULD include Retry-After. The word "temporary" is what distinguishes 503 from a hard failure — a 500 says the server itself is broken in a way it cannot describe.
Three properties of the definitions decide most real-world arguments:
Because the class digit changes who owns the fix, the status code you emit is a statement about your architecture. A rate limiter that answers "you are too fast" with 503 tells every well-behaved client in the world that the service is sick — polluting monitoring, misdirecting retries, and inviting pager alerts for a condition that needed no human at all.
The direct answer: read, in order, the status class, the Retry-After value, the rate-limit headers, the response body's error code, and the correlation with your own request rate — these five signals separate a quota rejection from a genuine outage in under a minute, before you touch a dashboard.
In practice you rarely see a bare status code. Cloud front doors, API gateways and provider-side throttles add headers and bodies, and reading them in the right order is faster than any external check. The signals:
The five-signal read works because each signal narrows the search space independently. In triage order: class tells you which side owns the problem, Retry-After tells you whether the sender expects you to come back, headers confirm whether a quota was the cause, the body names the exact limit, and your own telemetry rules out the possibility that someone else's traffic on your token is the real story.
The direct answer: 429 means "your pace is the problem," 503 means "my capacity is the problem," 502 means "my gateway reached something invalid," 504 means "my gateway timed out waiting," and 500 means "my code failed" — and no two of these justify the same client response.
Half of all wrong fixes start as wrong status-code reads, so the table comes with a conclusion: if your client retries a 429 without honouring Retry-After, you are amplifying the exact condition that triggered it; if your client does not retry a 503 that carries a Retry-After, you are leaving availability on the table. Match the row to the code you actually received before writing any handler.
| Code | Meaning | Who owns the fix | Retry behaviour |
|---|---|---|---|
| 429 | Too many requests — quota or pace exhausted | Client slows down or raises quota | Retry after honouring Retry-After with jitter |
| 503 | Service temporarily unavailable — overload or maintenance | Server side restores capacity | Retry with capped exponential backoff, respect Retry-After if present |
| 502 | Gateway received an invalid response from upstream | Server side — upstream is dead or malformed | Retry once at most; persistent 502s are an upstream incident |
| 504 | Gateway timed out waiting for upstream | Server side — upstream too slow under load | Retry idempotent requests with backoff; investigate upstream latency |
| 500 | Server-side exception or unhandled failure | Server side — code defect | Retry is gambling; alert instead |
Two boundary cases deserve prose. A 429 for a spend-limit problem — quota exhausted because the billing cap was hit, not because requests were too fast — needs a payment action, not a backoff, which is why reading the error body matters more than the status digit. And a 503 emitted by a load balancer because its health checks are failing is a different incident from a 503 emitted by the application itself: the first means the fleet is unhealthy, the second means the app chose to shed load. The Server header or an identifying error field usually tells you which layer is speaking.
The direct answer: a quota exceeded at the edge should return 429 with Retry-After, a dependency timeout under load should return 503 with Retry-After, a maintenance window should return 503 with an absolute-date Retry-After, and the other three states — burst rejection, queued overflow, health-check failure — each have exactly one correct code, and picking the wrong one converts a self-healing condition into a pager event.
A rate limiter is not one mechanism but six, and each state has a correct status code:
The unifying principle: 429 when the client's behaviour is the cause and a known moment of relief exists; 503 when the server's capacity is the cause. The table that follows is the summary engineers tend to pin next to the rate-limiter configuration, with the conclusion stated up front: if your table row's "correct code" column disagrees with what your gateway currently emits, that disagreement is an incident waiting for a quiet afternoon.
| Implementation state | Correct code | Retry-After | Common wrong emission |
|---|---|---|---|
| Token bucket exhausted (pace) | 429 | Next refill, in seconds | 503 — teaches clients the service is sick |
| Period quota exhausted | 429 | Reset timestamp | 503 — same harm, plus wrong SLO accounting |
| Concurrency cap reached | 503 (or 429 for tenant protection) | Short, seconds | 500 — clients alert instead of waiting |
| Dependency overload / load shedding | 503 | Shed window estimate | 429 — clients throttle themselves for a server problem |
| Scheduled maintenance | 503 | Absolute date-time | 503 without Retry-After — clients poll blindly |
| Health-check failure at balancer | 503 | Should be set, usually is not | 502 — misattributed as an upstream protocol fault |
The direct answer: a 429 or 503 without a Retry-After header forces every client to guess the wait, and guesses cluster at aggressive defaults — 1 second, often with no jitter — which synchronises retries into a thundering herd that can out-pressure the original spike the limiter was built to absorb.
RFC 6585 and RFC 9110 both use "SHOULD" for Retry-After, which in standards language means "ignore this only when you understand the full cost of doing so." The cost is concrete. A client that receives 429 without Retry-After picks a default — typically 1 second — and if hundreds of clients make the same guess in the same second, their synchronized retries land together, get rejected together, and retry again together. The herd does not disperse; it oscillates. Adding a single digit of random jitter to each client's wait is the cheapest known mitigation, but the better fix belongs to the server: emit the header, because the server is the only party that knows the true window.
The header supports two formats, and choosing correctly matters:
Retry-After: 30 — meaning "try again in 30 seconds." Right for token refills and load-shedding windows.Retry-After: Wed, 21 Oct 2026 07:28:00 GMT — meaning "the condition ends at this moment." Right for maintenance windows and quota resets, where an absolute moment is known.One caution from field experience: delta-seconds values computed from a rate window can be wrong by up to one full window if the client's clock and the server's window boundary disagree. When your limiter knows the reset boundary — and it always does — the absolute date format is the more defensible emission.
The direct answer: on 429 you should back off for exactly Retry-After — honouring it is your side of the contract — while on 503 you should retry with capped exponential backoff and jitter, treating Retry-After as a ceiling rather than a schedule, because a 503's promise is less certain than a 429's.
The asymmetry comes from who owns the condition. A 429's Retry-After is a contract term: the limiter knows when your token bucket refills and is telling you the exact moment asking again will work. Waiting longer is waste, waiting less is a new violation. A 503's Retry-After is an estimate about someone else's recovery: capacity may return sooner, or later, and the client's duty is bounded patience, not obedience.
A correct retry policy for both codes, as converged practice in major API client libraries as of October 2026:
The idempotency-key rule deserves emphasis in the AI-agent era: agents that retry AI API calls after timeouts without idempotency keys can duplicate side effects and double-spend quota, a failure pattern documented in production postmortems throughout 2026. The retry policy is not complete until the request it retries is safe to process twice.
The direct answer: when a load balancer emits a blanket 503 because one backend is unhealthy, every well-behaved client that honours Retry-After will retry simultaneously at the same moment, and the herd can hit harder than the original request spike — which is why the balancer's Retry-After should carry jitter baked into the value, not a constant every client will echo.
The scenario is common enough to have a name in postmortem culture. A fleet of 200 backends loses 10 to a bad deploy. The balancer, seeing capacity fall, begins shedding load with 503s. Each 503 carries Retry-After: 5. Ten thousand clients read "5", sleep exactly 5 seconds, and return as a single wave — arriving precisely when the balancer is still recovering. The wave is rejected again, sleeps another 5, and returns again in lockstep. Meanwhile the 190 healthy backends idle because the herd is synchronized against the shedding window rather than distributed across time.
Three mitigations, in order of effectiveness:
The same mathematics applies to your own API's clients, which is the practical reason to audit your 503 emissions: you cannot control every client's jitter discipline, but you can control whether your Retry-After values give them a reason to synchronize.
The direct answer: 429s are not errors — they are the rate limiter working — so your error-rate SLO should count 5xx only, track 429s as a quota-and-throttling metric with its own dashboard, and alert on the 429 rate crossing a client-experience threshold rather than on any 429 existing at all.
The most expensive category of status-code mistake is not technical but observability. A team that lumps 4xx and 5xx into one "error rate" metric will see a rate limiter doing its job — say, 2 percent of requests throttled during a traffic spike — as a service degradation, page an on-call engineer, and start investigating a healthy system while the real signal, whatever drove the traffic anomaly, goes unexamined. The fix is structural:
The 429-with-Retry-After ratio deserves a word of explanation: it is the single best leading indicator of how much retry traffic your limiter is converting into herd risk. A healthy limiter emits the header on every throttle; a gateway in front of it that strips headers — some do — silently converts your careful contract into blind client guessing, and the falling ratio is how you notice.
The direct answer: API gateways, CDNs and sidecar proxies frequently rewrite or strip Retry-After and rate-limit headers from upstream responses — sometimes to enforce their own throttle above yours, sometimes as a misconfiguration — and a client seeing a 429 without Retry-After should suspect the middle layer before blaming the origin.
The layering problem is structural. Your request crosses your client's retry layer, possibly a service mesh, the provider's edge, the provider's gateway, and only then your application's own limiter. Each hop can emit its own throttle or rewrite the one it forwards. Three cases dominate production incidents:
The diagnostic is a header audit from two vantage points: send one request from inside the network — origin-adjacent — and one from outside, and diff the response headers. The headers present inside but absent outside were stripped by the middle layer, and the layer that stripped them is the one to reconfigure. Teams that run this audit annually report fewer "mystery 429" tickets than teams that debug from the client side alone, because the client side can only ever see the final, possibly rewritten, envelope.
The direct answer: a rate limiter that emits 429 with Retry-After for quota states and 503 with Retry-After for capacity states — and never the reverse — is telling the truth, and truthful limiters produce self-healing client behaviour while untruthful ones convert every traffic spike into either a pager event or a thundering herd.
The six-state table and the five-signal triage sequence are the two artifacts worth carrying forward. For teams that want to rehearse both sides of the contract — a limiter emitting correct codes, and a client honouring them — the environment needs are modest: a small server to run nginx or an API gateway, a public address to test from outside, and enough root access to deliberately saturate a token bucket and watch the herd form and disperse. SurferCloud's ULightHost line fits this lab use case, with root access, dedicated IPv4, and entry configurations from $2.9 per month as of October 2026 per the current promotion page; the tiers and pricing details are on the ULightHost promotions page. A week of deliberate throttle-and-retry experiments on a machine you can break teaches more than a year of reading retry-policy documentation.
The status code is a statement about your architecture. Get the digit right, attach the header, add the jitter — and the two codes stop being a trivia question and start being the contract that keeps your clients well-behaved without a single human in the loop. That contract is worth rehearsing, and an inexpensive always-on instance is the cheapest rehearsal space there is.
Is a 429 an error I should fix on my side or theirs?
Yours. The 4xx class digit means the client's behaviour caused the condition — too fast, too many, or a spend cap reached — and the fix is backoff, quota increase, or payment, respectively. Read the error body to distinguish the three.
Should I retry on a 503 or treat it as an outage?
Retry, with capped exponential backoff and jitter, for a maximum of 3-5 attempts. A 503 is by definition temporary. If Retry-After is present, treat it as a ceiling on your wait, not a schedule to obey exactly — the server's estimate of its own recovery is less certain than a quota window.
Why do I get a 429 with no Retry-After header?
Usually a middle layer stripped it. Audit response headers from inside and outside the network and diff them; headers present on the inside but missing outside were removed by a gateway, CDN, or sidecar, and that layer is the one to reconfigure. Retry-After is a SHOULD in the RFCs, so some origins also simply omit it.
What is the difference between 502 and 503 in a load balancer's logs?
A 502 means the balancer reached an upstream and the upstream's answer was invalid or the connection failed — the upstream is speaking, but wrongly. A 503 means the balancer is declining to forward at all — no healthy upstream, deliberate load shedding, or maintenance. 502 is an upstream incident; 503 is a capacity or availability decision.
Can I return 503 from my rate limiter to make clients slow down gently?
You can, but you are lying to them. A 503 tells clients your service is unhealthy, which pollutes their monitoring, misdirects their retries into outage playbooks, and invites pager alerts for what is actually their own request pace. Emit 429 with Retry-After instead — that is the code's entire reason to exist.
How do I stop my retries from synchronizing into a thundering herd?
Two layers: server-side, randomize Retry-After values within a band so returning clients spread across it; client-side, apply full jitter to backoff waits so independently written clients have no reason to agree on a return time. Add a retry budget — a cap on the retry ratio, commonly 20 percent — as the backstop.
Should my error-rate SLO include 429s?
No. Count 5xx against availability; track 429s as a separate quota-and-throttling metric. A rate limiter doing its job is not an outage, and teams that conflate the two page engineers to investigate healthy systems while the actual anomaly goes unexamined.
Do all the big AI APIs use 429 for rate limits?
As of October 2026, yes — OpenAI, Anthropic and Google Gemini all signal quota exhaustion with 429, though their error bodies differ (rate_limit_error types, RESOURCE_EXHAUSTED codes) and some share the 429 status with spend-limit conditions that need payment rather than patience. Read the body, not just the digit.
With Red Hat discontinuing CentOS in favor of CentOS St...
6 Tbps DDoS Attack Hits Hosting Provider — Why VPS Bu...
Introduction Domain flipping remains one of the easi...