HTTP 429 or 503: Which One You Retry, Which Needs an Idempotency Key, and What Retry-After Tells You (2026)
Both statuses say come back later, and both are usually retried, but they mean different things and one of them is safe to repeat unchanged.
A 429 is about you: your client exceeded its allowance and nothing was processed, so the identical request is safe to retry. A 503 is about the server: it is unavailable for everyone, and the request may have partly executed before failing, so a retry needs an idempotency key. Both warrant backoff; only 429 is fixed by slowing your own client down.
What each status commits to
A 429 carries an implicit guarantee that the request was refused before processing. That guarantee is what makes a bare retry safe. A 503 carries no such promise: the server may have accepted the work and failed while completing it, so a retry can duplicate an effect.
Distinguishing them in monitoring
Folding both into one error rate hides the only actionable difference. A rising 429 rate means your traffic changed or a limit did, and the fix is on your side. A rising 503 rate means the dependency is unhealthy, and no amount of client tuning helps.
The status that is neither
A 500 is worse than both, because it commits to nothing at all. Treat it like a 503 for retry purposes and require idempotency, but escalate it faster: a 503 is usually deliberate, while a 500 is usually a bug.
What each one implies about capacity
A 429 means the service is healthy and enforcing fairness, so the capacity exists and is simply not yours right now. A 503 means capacity is absent for everyone, which is why adding retries to a 503 storm makes recovery slower: every retry consumes attention the service needs to come back. The two therefore call for different retry budgets even though both use backoff.
The guarantee each status makes about your request
A 429 carries an implicit promise that the request was refused before processing began. The limiter sits in front of the handler, counts, and rejects — nothing downstream ran. That promise is the whole reason a bare retry is safe. A 503 makes no such promise: it is emitted by load shedders, by health checks failing mid-request, by a dependency timing out after the work started, and by a deployment draining connections. Some of those mean nothing happened; others mean the work was half applied. From the client side they are indistinguishable.
Why one of them is fixed by slowing down and the other is not
A 429 is about you specifically: capacity exists and you are over your share of it. Slowing your client down resolves it entirely, and adding retries without slowing down resolves nothing. A 503 is about everyone: capacity is absent. Slowing down does not help you get served any sooner, and retrying harder actively competes with the recovery you are waiting for. This is the practical difference that survives every other detail.
The boundary case: a 503 that carries Retry-After
A 503 with a Retry-After header is a deliberate, planned rejection — maintenance, a deployment drain, a scheduled window. Treat it as authoritative and wait exactly that long. A 503 without one is usually an overload or a crash, where nobody has decided when service resumes, and there the correct behaviour is a long backoff with a low attempt ceiling. The presence of the header, not the status code, is what tells you whether someone is in control of the situation.
Where 500 sits, and why it is worse than both
A 500 commits to nothing at all: not that the request was refused, not that it was processed, not that anyone knows. It is a bug rather than a policy, which is why the retry treatment resembles 503 while the alerting treatment does not. A rising 503 rate is often expected during a deploy; a rising 500 rate never is. Folding them together means the one that always needs a human is hidden by the one that frequently does not.
The gateway pair: 502 and 504
A 502 says an upstream returned something unintelligible; a 504 says it did not answer in time. Both mean a proxy is speaking on behalf of a server you cannot see, and crucially both can occur after the upstream received and processed the request. A 504 in particular often means the work completed and the response was lost, which is the most dangerous case for an unguarded retry: the operation succeeded and you are about to run it again.
Distinguishing them in monitoring
Track 429 as its own series, separate from 5xx, and separate 500 from 502, 503 and 504. Folding everything into one error rate hides the only distinctions that change what you do: whether the fix is on your side, whether a human is needed, and whether a retry is safe. A dashboard with a single error line will show the same shape for a capacity problem, a deploy and a code bug.
What each one implies about capacity planning
A steady background of 429s means your traffic has grown into its allowance and the conversation is with the provider or with your own pacing. A steady background of 503s means the dependency is running at its limit and the conversation is about its capacity, not yours. Reading the first as the second leads to buying capacity that was never the constraint.
Retry budgets differ even though both use backoff
Because a 429 comes from a healthy service, it can be retried fairly assertively within the pacing the server asks for — the work will be accepted as soon as the window allows. A 503 deserves fewer attempts and a longer ceiling, since every attempt consumes attention the service needs to recover. Applying one retry policy to both either hammers a struggling service or gives up on a healthy one.
Idempotency is required for one and optional for the other
Retrying a 429 without an idempotency key is safe because nothing was processed. Retrying a 503, a 500, a 502 or a 504 without one risks duplicating an effect that partly landed. In practice retry logic never stays narrow — a timeout gets added, then a 502 — so the safe default is to add the key when you add the retry rather than to reason case by case about which statuses are currently covered.
What the client should surface to a user
A 429 has an honest user-facing message: this is temporary and self-resolving, try again shortly. A 503 deserves a different one, because it may not be short and nothing the user does affects it. A 500 deserves neither: it is a defect, and telling a user to retry a bug wastes their time. Collapsing all three into "something went wrong" throws away information the user could have acted on.
Which of these should ever reach a health check
A health check must not retry any of them. Its job is to report the current state, and a retry converts an unhealthy signal into a delayed healthy one — the failure it exists to surface becomes invisible for exactly as long as the retry budget lasts. This is the one place in a client where the correct number of attempts is zero.
A note on 429 from an intermediary
Not every 429 comes from the API. Edge layers, WAFs and CDNs emit them too, usually per-IP rather than per-credential and usually without the rate-limit headers the API would have included. A 429 with no accompanying limit headers is a strong hint that the request never reached the application, which changes the remedy: spreading source addresses helps, and spending time tuning your per-key pacing does not.
Comparing the statuses
Comparing the statuses
| 429 | 503 | 500 | 504 |
| About | your client | the server | the request | an upstream |
| Was it processed | no, guaranteed | unknown | unknown | often yes |
| Bare retry safe | yes | no | no | no |
| Retry-After typical | yes | sometimes | rarely | rarely |
| Fixed by slowing down | yes | no | no | no |
| Expected during a deploy | no | yes | no | sometimes |
| Needs a human | rarely | sometimes | always | usually |
Retry budget by status
Retry budget by status
| Status | Attempts | Ceiling | Idempotency key |
| 429 with Retry-After | as needed | honour the header | not required |
| 429 without Retry-After | 3-5 | 30 s | not required |
| 503 with Retry-After | 2-3 | honour the header | required |
| 503 without Retry-After | 2-3 | 2-5 min | required |
| 500 | 1-2 | 30 s | required |
| 502 / 504 | 1-2 | 30 s | required |
What the presence of Retry-After tells you
What the presence of Retry-After tells you
| Status | Header present | Reading |
| 429 | yes | Limiter knows exactly when your window resets |
| 429 | no | Often an edge layer; limit may be per-IP |
| 503 | yes | Planned: maintenance, drain, scheduled window |
| 503 | no | Unplanned: overload or crash, nobody is in control |
| 500 | either | Header is meaningless here; this is a defect |
How each should be monitored
How each should be monitored
| Status | Alert on | Do not alert on |
| 429 | step change in rate | steady background rate |
| 503 | rate outside deploy windows | brief spike during a deploy |
| 500 | any sustained rate | a single isolated occurrence |
| 502 / 504 | rate, and latency alongside it | one-off during upstream restart |
What to tell the user
What to tell the user
| Status | User-facing message | Actionable by them |
| 429 | Busy right now, try again shortly | yes, by waiting |
| 503 | Service unavailable, we are working on it | no |
| 500 | Something broke on our side | no; retrying wastes their time |
| 504 | Timed out; the action may have completed | yes, by checking before repeating |
Misreadings and what they cost
Misreadings and what they cost
| Misreading | Consequence |
| Treating 503 like 429 and retrying hard | Competes with the recovery you are waiting for |
| Treating 429 like 500 and alerting | Pages a human for backpressure working correctly |
| Retrying 504 without an idempotency key | Duplicates work that already completed |
| One error rate for all of them | A code bug hides behind an expected deploy spike |
| Retrying inside a health check | The failure the check exists to report is hidden |
Key facts
- HTTP 429 guarantees the request was refused before processing, which is what makes retrying the identical request safe without an idempotency key.
- HTTP 503 makes no such guarantee, so a retry after 503 needs an idempotency key to avoid duplicating a partly-applied effect.
- 429 is specific to one client and is resolved by slowing that client down; 503 affects everyone and cannot be resolved from the client side.
- Retrying a 503 harder competes with the recovery you are waiting for, which is why its retry budget should be smaller than a 429 budget.
- A 503 carrying Retry-After is a planned rejection — maintenance or a drain — and the header is authoritative.
- A 503 without Retry-After is usually an unplanned overload, where nobody has decided when service resumes.
- A 504 frequently means the upstream completed the work and the response was lost, which makes an unguarded retry the most dangerous of all.
- A 500 commits to nothing about whether the work was applied, so it is retried like a 503 but escalated like a defect.
- A rising 503 rate is often expected during a deploy; a rising 500 rate never is, so folding them together hides the one that needs a human.
- A 429 arriving without any rate-limit headers usually came from an edge layer rather than the API, which means the limit is likely per-IP.
- Tracking 429 and 5xx as a single error rate hides the only distinctions that change what you do next.
- A health check must never retry any of these, because a retry converts an unhealthy signal into a delayed healthy one.
- Collapsing 429, 503 and 500 into one user-facing message discards information the user could have acted on.
- A steady background of 429s is a conversation about your own pacing; a steady background of 503s is a conversation about the dependency capacity.
Frequently asked questions
Should a 429 page an on-call engineer?
Not by itself. A steady rate is backpressure working as designed. A step change is worth an alert, because it means either your traffic shape or the provider limit moved, and those have different fixes.
Can I retry a 503 without an idempotency key?
Only for naturally idempotent operations. For anything that creates or increments, a 503 leaves you unable to know whether the work landed, which is exactly the situation keys exist for.
Which is worse to receive, 503 or 500?
500, in almost every respect. A 503 is usually a deliberate signal from a system that knows it is unavailable; a 500 is a defect that nobody planned and that may have left state half written.
Why is 504 singled out as dangerous?
Because the most common cause is an upstream that finished the work while the proxy gave up waiting. The client sees a failure for an operation that actually succeeded, and a bare retry runs it twice.
I get 429s with no rate-limit headers at all. What does that mean?
Usually that an edge layer refused you before the request reached the API. Those limits are typically per-IP rather than per-credential, so the remedy is different: spreading source addresses helps and tuning per-key pacing does not.
Should the retry budget really differ between 429 and 503?
Yes. A 429 comes from a healthy service that will accept the work as soon as your window resets, so retrying within the stated pacing is fine. A 503 comes from a service that is struggling, and every attempt consumes capacity it needs to recover.
Is it acceptable to show the user the same error for all of these?
It is common and it throws away information. A 429 resolves itself if they wait; a 503 may not resolve soon and waiting is pointless; a 500 is a bug and retrying wastes their time. Three different situations, three different honest messages.
How should health checks treat these statuses?
Report them, never retry them. A retry inside a health check delays the unhealthy signal by exactly the retry budget, which is the one thing a health check must not do.
Does a 503 during a deployment need an alert?
Not during the window, and yes outside it. That distinction requires the deploy to be visible to the alerting system; without it you either alert on every deploy or suppress the alerts that matter.
What if a service returns 500 for rate limiting?
Some do, and it is a defect on their side that becomes your problem: you lose the guarantee that nothing was processed and must treat every retry as unsafe. If the body or headers reveal the real cause, branch on that; otherwise apply the stricter treatment.
Should 429 count toward an error budget or SLO?
Usually not as an error, because it is the system working as designed. It belongs in a saturation metric instead. Counting it as failure makes a well-behaved limiter look like an outage.
How do I tell a planned 503 from an overload?
By the Retry-After header more than by anything else. A planned rejection knows when it ends and says so; an overload does not know and cannot.
Can retrying a 429 ever make things worse?
Yes, when many clients retry in lockstep. The status is safe to repeat, but the timing is not: without jitter, every client that was limited together returns together and reproduces the burst.
Which of these should ever be cached?
None as a successful response, but a client-side note that a limit is currently exhausted is worth holding, so other calls in the same process do not each discover the same 429 independently.
Machine-readable copy of this page:
/guide/difference-between-429-and-503.md