Skip to content
Skip to main content
Claude API request throughput squeezed through a narrow constriction, illustrating 429 rate limits and throttling
9 min readBy Carlos Aragon

Claude API 429 Rate Limits: How to Fix Them in Production

A Claude API 429 isn't one limit, it's three: requests per minute, input tokens per minute, and output tokens per minute — enforced per model, per organization, on a bucket that refills continuously instead of resetting on the minute. The response already tells you which one you hit and how long to wait. Fixing it in production comes down to four moves: honor retry-after before you reach for backoff, read the remaining-headers so you throttle beforethe error, share one limiter across every worker, and cache — because cached input tokens don't count against your ITPM ceiling.

The Mistake: Treating 429 as One Number

Nearly every “fix Claude 429” article I've read tells you to add exponential backoff and move on. That's the last 10% of the fix. The first 90% is knowing which of the three limits you crossed, because each one has a completely different remedy.

  • RPM (requests per minute): too many calls, regardless of size. Fix with concurrency control — fewer in-flight requests, or batching several items into one call.
  • ITPM (input tokens per minute): your prompts are too big or too repetitive. Fix with prompt caching and by trimming context, not by slowing down.
  • OTPM (output tokens per minute): you're generating too much text per minute. Fix by shortening responses or spreading generation out. Note that max_tokens does not count against OTPM — only tokens actually produced do, so a generous max_tokens costs you nothing here.

Slowing your request rate to fix an ITPM problem is the classic wrong move. You cut throughput, the bill stays the same, and the 429s keep landing because a smaller number of oversized prompts still saturates the input budget.

The one that catches teams out:

Limits are enforced at the organization level, not per API key. Your batch job, your production app, your teammate's notebook and every scheduled cron in that org all draw from the same bucket. Issuing a second API key does not give you a second budget — that's what Workspaces are for, and they only ever divide the org limit, never expand it.

The Headers Almost Nobody Logs

Every response from the Messages API — not just the failures — carries your current rate limit state. Most clients throw it away. Log these four and you can see a 429 coming a full minute before it arrives:

HeaderWhat to do with it
retry-afterSeconds to wait. Retrying earlier is documented to fail — it's a wasted request that keeps your bucket pinned.
anthropic-ratelimit-input-tokens-remainingInput headroom this minute, rounded to the nearest thousand. Your early-warning signal for ITPM.
anthropic-ratelimit-output-tokens-remainingSame for OTPM. Long-generation workloads hit this one first.
anthropic-ratelimit-requests-resetRFC 3339 timestamp for full replenishment. Use it instead of a hard-coded sleep(60).

There's a matching -limitheader for each, so you can compute a live utilization percentage without ever hard-coding your tier into the app. That matters more than it sounds: tiers move as your usage history grows, and a constant you set six months ago is a constant that's now wrong.

The one subtlety worth knowing: the generic anthropic-ratelimit-tokens-* headers report whichever limit is most restrictive right now. If a Workspace cap is biting, you see the Workspace numbers there, not the org numbers. Prefer the explicit input-tokens and output-tokens headers when you want to know which side of the ledger is actually full.

Retry Correctly: retry-after First, Jittered Backoff Second

Exponential backoff on its own has a failure mode that shows up the moment you run more than one worker: every worker gets 429'd at the same instant, every worker doubles its wait by the same schedule, and they all come back together. You've built a thundering herd with extra steps. Jitter is what breaks the synchronization, and it isn't optional.

import random, time

def call_with_retry(send, max_attempts=5):
    for attempt in range(max_attempts):
        resp = send()
        if resp.status_code != 429:
            return resp

        # 1. The server told us. Believe it.
        wait = resp.headers.get("retry-after")
        if wait is not None:
            time.sleep(float(wait))
            continue

        # 2. No header? Backoff WITH jitter, capped.
        backoff = min(2 ** attempt, 60)
        time.sleep(random.uniform(0, backoff))   # full jitter

    raise RuntimeError("rate limited after %d attempts" % max_attempts)

Three rules I hold to. Cap the attempts — an uncapped retry loop against a limit you genuinely can't clear is just a slower outage. Cap the wait at around 60 seconds, because the bucket refills continuously and waiting five minutes buys you nothing. And never retry a 400; only 429 and 5xx are worth a second attempt. If you're using an official SDK, check its built-in retry setting before you write any of this — most of them already do the first part and you only need to tune the count.

This is the same “transient versus dead” distinction I wrote about in the cost controls I put on every autopilot loop. A 429 is transient. A 401 is dead. Classifying them the same way is how an agent loop runs all weekend achieving nothing.

The Cheapest Rate Limit Increase Is Prompt Caching

This is the part most 429 guides miss entirely, and it's the highest-leverage fix on the list. On current Claude models, cached input tokens do not count toward your ITPM limit. Only two of the three input counters do:

  • input_tokens — counts toward ITPM. Note this is only the tokens after your last cache breakpoint, not your whole prompt.
  • cache_creation_input_tokens — counts toward ITPM. You pay the limit cost once, when you write the cache.
  • cache_read_input_tokens — does NOT count toward ITPM on current models. This is the free headroom.

Anthropic's own worked example makes the size of this obvious: with a 2,000,000 ITPM limit and an 80% cache hit rate, you can effectively push 10,000,000 total input tokens per minute — 2M uncached plus 8M cached. That is a 5× throughput increase from a caching change, with no tier upgrade and no support ticket.

Reframe it:

Prompt caching is usually sold as a cost optimization. It is also a rate limitoptimization, and in a workload with a big fixed prefix — system prompt, tool definitions, a reference document, conversation history — those are the exact same tokens you're re-sending every call. Cache the prefix and the same limit carries several times the traffic.

The catch is your breakpoint placement. Everything before the last cache breakpoint gets cached; everything after it is fresh input. Put the breakpoint after the stable prefix and before the variable user turn, and confirm it's working by watching cache_read_input_tokens in the usage block on real responses — not by assuming. I broke down the setup and the actual savings in my guide to Anthropic prompt caching.

Throttle Before the 429, and Share the Limiter

A 429 is a failure you were told about in advance and chose not to act on. The remaining-headers give you a live gauge; the only work is wiring the sender to that gauge instead of to a fixed sleep.

# after every successful call, adapt instead of guessing
inp = int(resp.headers["anthropic-ratelimit-input-tokens-remaining"])
lim = int(resp.headers["anthropic-ratelimit-input-tokens-limit"])

if inp < lim * 0.10:        # under 10% headroom
    concurrency = 1         # single file until it recovers
elif inp < lim * 0.25:
    concurrency = max(1, concurrency // 2)
elif inp > lim * 0.60:
    concurrency = min(concurrency + 1, MAX_WORKERS)

Then the part people get wrong at scale: a per-process limiter is not a limiter. Rate limits are enforced per organization, so eight workers each politely capped at 5 concurrent requests is 40 concurrent requests as far as the API is concerned. Every horizontally-scaled pipeline I've debugged that “mysteriously” 429s under load has had exactly this bug. The limiter has to live somewhere all the workers can see it — a Redis token bucket, a queue with a fixed number of consumers, or a single gateway process that owns all outbound calls.

In n8n the equivalent knobs are the batch size and delay settings on the AI nodes plus Retry On Fail at the node level, but the same trap applies the moment you run n8n in queue mode with multiple workers — per-node throttling doesn't coordinate across workers. And whatever you build, put the 429 rate on a dashboard; tracing your agent runs is how you find out you're running at 95% utilization before a traffic spike finds out for you.

The 429 That Isn't Your Limit

Two situations produce a 429 while your dashboard swears you have headroom.

The bucket, not the minute.Anthropic uses a token bucket, which refills continuously rather than resetting at the top of each minute. A 60 RPM limit is effectively 1 request per second — so 60 requests fired in the first two seconds of a minute will get rejected even though the per-minute average is exactly at the limit. Smooth your sends; don't schedule everything on the same tick. This is the single most common “but I'm under my limit” case.

Acceleration limits.Anthropic separately documents that a sharp increase in an organization's usage can trigger 429s independent of your tier limits. If you switch a big migration or a new customer on all at once, ramp it: minutes of gradual increase, not an instant step change. It looks like a bug and it isn't one.

Worth knowing too: limits are per model, so an Opus workload and a Haiku workload draw from separate buckets and can run simultaneously up to each ceiling. Routing your cheap steps to a smaller model isn't only a cost optimization — it moves that traffic off the frontier model's rate limit entirely. Two wins from one change.

When to Stop Optimizing and Change Lanes

At some point the answer isn't a better retry policy. Three escape hatches, in the order I reach for them:

  • Move non-interactive work to the Message Batches API. It has its own limits, shared across models, so bulk classification and scheduled jobs stop competing with your live traffic for ITPM. If a user isn't waiting on it, it doesn't belong in your real-time path.
  • Request a limit increase from the Console. It's a form on the Rate limits page, and it goes far better when you bring your peak input and output tokens per minute per model and your cache hit rate — which you'll have, because you started logging the headers.
  • Split by Workspace to protect services from each other. Workspace limits carve up the org limit rather than adding to it, but that's exactly what you want when one runaway backfill would otherwise starve your production app.

I covered the economics of the batch route separately in the Claude Batch API cost breakdown, and the official numbers for every tier live in Anthropic's rate limits documentation. Check them rather than trusting a blog post's table — including this one. Tiers change.

The Short Version

  • A 429 is three different limits — RPM, ITPM, OTPM — with three different fixes. Read the error body before you touch your retry code.
  • Honor retry-after. Retrying earlier is documented to fail, so it's a wasted request that keeps the bucket pinned.
  • Backoff without jitter synchronizes your workers into a thundering herd. Add full jitter, cap the wait near 60 seconds, cap the attempts.
  • Log anthropic-ratelimit-*-remaining on every response and throttle before the 429 fires, not after.
  • Limits are per organization. A per-process limiter multiplied by your worker count is not a limiter.
  • Prompt caching raises your effective ITPM ceiling, because cache reads don't count toward it — up to ~5× at an 80% hit rate.
  • max_tokens doesn't count against OTPM. Only generated tokens do. Set it generously.
  • Under your limit and still 429'd? It's the token bucket (bursts) or acceleration limits (ramp too fast).

Getting 429s in Production and Losing Requests?

I build and fix production AI systems on the Claude API, n8n and Supabase — shared limiters, cache-aware prompts, batch pipelines, and the observability to see a limit coming before your users do. If your workload is fighting its rate limit instead of using it, let's talk.

Related Posts