Skip to content

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-after header — 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", no retry-after, and error.details.error_code set to enforced_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_error and 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 with retry-after instead.
  • 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.

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.