API Error: Rate limit reached
Your request was well-formed, authenticated and routed correctly. Something on the account side then declined to run it right now. That rules out a bad API key, a malformed body, and a platform outage — none of those print this line. What the line does not tell you is which limit refused you, and the four candidates have four different correct responses, one of which is “stop retrying entirely.”
Data as of 2026-09. Vendor limits and defaults change; check the official docs for current values before acting on any number below.
There is no reset boundary to wait for
The first wrong mental model to drop: rate limits on the Claude API are per-organization and per-model, and they are enforced with a token bucket. Capacity replenishes continuously rather than resetting on a fixed boundary. There is no top of the hour at which your allowance comes back. If you are sitting out a rate limit, you are waiting for the bucket to refill by the amount your next request needs — which is usually a much shorter wait than the one people give themselves, and which means “I’ll try again after the hour turns” is both slower than necessary and no guarantee.
Three limits are counted separately: requests per minute, input tokens per minute, and output tokens per minute. You can be nowhere near two of them and still be refused by the third, and the error message names none of them.
The two fixes everyone reaches for first, and why they miss
Lowering max_tokens does nothing for a rate limit. Output tokens are
counted as they are actually produced, so max_tokens does not factor into
OTPM at all. A high ceiling you never reach carries no rate-limit penalty. If
you cut max_tokens and the errors continued, that is not bad luck — the knob
was never connected to the thing refusing you.
Trimming the prompt often does nothing either, for a subtler reason. For most Claude models, only uncached input tokens count toward the input-token limit: tokens after the last cache breakpoint count, tokens written to the cache count, and tokens read back from the cache do not. Claude Haiku 3.5 is the documented exception — it does count cache reads. So if your prompt is mostly a long cached prefix, shaving it down changes a number that was never being charged against ITPM in the first place. What moves the needle is the volume of new input, and how many requests you fire in parallel.
Is retrying useful?
Yes for three of the four cases — and the response headers tell you which one you are in before you burn an attempt.
If the response carries a retry-after header, honor it and retry after that
many seconds. The official SDKs already do this: they retry transient failures,
including rate limits, with exponential backoff, twice by default, honoring
retry-after when it is present. Do not hard-code a wait of N seconds of your
own — the published guidance is exponential backoff, and no base delay is
documented for you to copy.
If there is no retry-after header, treat that absence as a signal rather
than an omission. It is the marker of the one case where retrying is futile, and
where the SDK’s automatic retries will fail exactly as your manual ones do. Two
identical failures with no retry-after means stop and read the next section
instead of hitting Enter again.
Four limits, four tests
Every one of these prints a variation of the same refusal, so match yours by what comes back with it, not by what it says.
- 429 with a
retry-afterheader — an ordinary rate limit. You are pushing more requests or tokens per minute than your tier allows for that model. Wait the stated interval, then reduce concurrency. - 429,
error.type: "rate_limit_error", noretry-after, anderror.details.error_codeset toenforced_spend_limit_reached— your organization hit its monthly spend cap. Retrying keeps failing until access resumes at 00:00 UTC on the first day of the next month, or until the tier is raised. The caps are $500 USD on Start, $1,000 USD on Build and $200,000 USD on Scale; the Custom tier has no cap. - HTTP 400 with
invalid_request_errorand a message beginning “You have reached your specified API usage limits” — a limit you configured. Note the status: this one is not even a 429, so any handler keyed on 429 will mishandle it. Claude Code workspace limits can surface as a 429 withretry-afterinstead. - 429 right after a sharp ramp in traffic, while the headers still show headroom — acceleration limits, which are separate from the published tier table. The test is the shape of your traffic in the minutes before the error, not its total. Ramp up gradually and it clears.
One more case that is not yours at all: a 529 overloaded_error reflects
platform-wide traffic, not your organization’s usage. If the refusal says the
server is limiting requests and explicitly disclaims your usage limit, you are
on a different page — see
server-side throttling that is not your usage limit,
where waiting for a quota reset is the wrong move by construction.
Fix by scenario
- Ordinary rate limit — cut concurrency first, then batch. Per-minute limits are about arrival rate; halving the number of in-flight requests is a faster lever than rewriting prompts. For bulk work, the Message Batches API has its own separate limits.
- Monthly spend cap — nothing client-side will help. Raise the tier, or wait for the reset at the start of the next month. Any retry logic you add here is a loop that burns time to reproduce a certainty.
- A limit you set — change it where you set it, and update any handler that assumes rate limiting always arrives as a 429.
- Acceleration limit — introduce a ramp. Going from idle to full concurrency in one step is the trigger.
- Subscription plan rather than an organization API key — the counters above are the API’s, and they are not the same accounting as a plan’s usage allowance. The Claude Code usage limit calculator is built for that case and will tell you whether your consumption is anywhere near the plan’s.
Read the headers before you change anything
Every rate-limited response carries retry-after plus the
anthropic-ratelimit-{requests,tokens,input-tokens,output-tokens}-{limit,remaining,reset}
family. Two details in there trip people up. The reset values are RFC 3339
timestamps, not durations — you subtract, you do not sleep on them directly. And
remaining token counts are rounded to the nearest thousand, so a remaining
count that reads zero is not proof that you were at exactly zero. Also note that
the anthropic-ratelimit-tokens-* family reports whichever limit is currently
most restrictive, so the number you are reading may be describing a dimension
other than the one you assume.
How to confirm it’s fixed
Not “one request went through.” A single success after a rate limit is indistinguishable from the bucket having refilled while you were reading this.
Re-run the workload that failed, at the same concurrency, for long enough to
cover more than one minute of traffic, and watch
anthropic-ratelimit-*-remaining stay above zero for the whole run. That is the
falsifiable version. For the spend-cap case the test is different and simpler:
the fix is confirmed the first time a request that previously returned a 429
without retry-after returns a normal response — nothing else can produce
that transition.
Related errors
If the message explicitly says the limit is not yours, read
the server-side throttling page
instead; the fix there is about the rate you send at, not the quota you hold. If
the failure arrives as a 500 with a request_id attached, your request reached
the model service and something inside it broke — that is
an internal server error,
and the request id is the only part worth keeping. If it never got that far —
no request id, an upstream named in the message — it is
a 503 no healthy upstream,
a routing problem in which your limits play no part. And if you are on a plan
rather than a metered API key, start with the
usage limit calculator before
assuming any of the numbers above apply to you.