API rate limits explained — and what to do when you hit one

Rate limits are not a single number. They are enforced on three axes at once, per model, and most "we are being throttled" incidents are actually unbounded parallelism or missing backoff rather than a ceiling that is too low.

In short: Rate limits are enforced on requests per minute, tokens per minute and concurrent requests, and they are per-model rather than per-account. A 429 is a signal to back off and retry, not to change model And retrying without exponential backoff is what turns a brief limit into an outage.

The three axes

AxisWhat it countsWhat hits it first
Requests per minuteNumber of API callsChatbots, autocomplete, high-frequency polling
Input tokens per minuteTokens you sendAgent loops, long-context applications
Output tokens per minuteTokens generatedLong-form generation, batch summarisation

Your real ceiling is whichever axis you reach first. An application that sends few but very large requests will hit the token limit long before the request limit, which is why "but we only make 20 calls a minute" is not a defence.

What the response tells you

Rate limit headers come back on responses and tell you how much quota remains on each axis. Log them — it turns a mysterious throttling incident into a number you can act on:

Don't have an account yet? You need an API key from the gateway before the steps below will work — how the discount works and what you take on. Create one free → (no prepay, $1 minimum top-up)
import requests, time, random

def call_with_backoff(payload, max_retries=6):
    delay = 1.0
    for attempt in range(max_retries):
        r = requests.post("https://aicomp.ai/v1/messages", headers=HEADERS, json=payload, timeout=60)
        if r.status_code != 429:
            return r
        retry_after = float(r.headers.get("retry-after", delay))
        time.sleep(retry_after + random.uniform(0, 0.3))   # jitter
        delay *= 2
    raise RuntimeError("rate limited after retries")

Respect retry-after when it is present; fall back to exponential backoff when it is not. Add jitter so parallel workers do not retry in lockstep.

The fix most people actually need

Before requesting a higher limit, check concurrency on your side. Fanning out hundreds of parallel requests against a per-minute token budget will trip the limit no matter how high the ceiling is. A small semaphore usually resolves it:

from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=8) as ex:   # bound the parallelism
    results = list(ex.map(process_one, items))

Reducing tokens also raises throughput

Under a token-per-minute cap, sending less per request means getting through more requests. Compacting conversation history and trimming irrelevant context is therefore a throughput fix as well as a cost fix — see how to reduce Claude API cost.

Cost note. Rate limits and cost are the same problem from two directions: both are driven by how many tokens you send and how often. Fixing one usually improves the other.

Errors you will see

StatusMeaningAction
429Rate limit reachedBack off and retry with jitter
529Upstream overloadedSame retry logic; not caused by your quota
500Server errorRetry with backoff; alert if persistent
400Invalid requestDo not retry — fix the payload

Rate-limit failures and what each one is actually telling you

SymptomWhat is actually happeningHow to confirmFix
429s arrive in bursts, not under steady loadLimits are usually per-minute and per-token; a batch job can exhaust a minute's allowance instantly.Plot requests per minute alongside the errors, bursts align with job starts.Spread batch work with a rate limiter instead of firing it concurrently.
Retries make it worse instead of betterImmediate retries consume the same window that just refused you, so every retry deepens the hole.Watch whether error rate rises after you add retries.Back off exponentially with jitter, and honour any retry-after value returned.
One service starves anotherSeveral services share one key, so one noisy caller consumes the shared allowance.Split usage by model and by time; a single caller's spike will be visible.Issue a separate key per service, which also makes spend attributable.
Limits hit sooner than the request count suggestsLong-context requests consume a token budget that is separate from the request count.Compare input tokens per request against the errors, not just the request rate.Trim context, or throttle on tokens rather than on requests.
Streaming connections appear to leak slotsA stream holds a connection for its whole duration, occupying concurrency that request-count limits do not model.Count concurrent open streams at the moment errors begin.Cap concurrent streams and close them explicitly on error paths.
The documented limit does not match what you observeLimits vary by model and by account tier, and are not uniform across a catalogue.Test one model at a time to isolate which limit you are actually hitting.Treat published numbers as a starting point and measure your own ceiling.

When this is the wrong move

Three fixes that make rate limiting worse, and one that does not address the cause

SituationWhy it breaksDo this instead
You are about to raise concurrency to fix a 429Adding callers to a saturated window converts throttling into a queue that hides the real limit.Reduce concurrency, then add back slowly while watching the error rate.
You plan to retry indefinitelyUnbounded retries convert a rate limit into an unbounded bill.Cap attempts, back off, and surface the failure to a queue you can replay later.
You are sharing one key across environmentsProduction and development compete for the same allowance, and neither is attributable.Separate keys per environment before you tune anything else.
You are about to switch models to escape a limitLimits are per model and per tier; a different model has a different limit, not a larger one.Identify which axis you are hitting first (switching models does not change your request rate).

FAQ

What are the Claude API rate limits?

They are enforced on three axes at once, requests per minute, input tokens per minute and output tokens per minute And they are set per model rather than per account. Your effective ceiling is whichever axis you hit first, and it changes as your usage tier changes.

How do I know which limit I hit?

The error response names the limit, and the response headers carry the remaining quota for each axis. Log those headers on every request; guessing is what makes rate limit incidents drag on.

What is the correct way to handle a 429?

Exponential backoff with jitter. Retrying immediately in a tight loop makes the problem worse and can extend the throttling window. Cap the total retry time and surface a clear error after that.

Does a rate limit mean I should switch model?

No. Switching changes the per-token cost, not the limit you are hitting, and different models have different ceilings. Fix the retry logic first, then reduce tokens per request if you are still constrained.

How do I get a higher limit?

Request a tier increase from the provider with real projected volume. In the meantime, concurrency control on your side is the practical lever, most self-inflicted 429s come from unbounded parallelism, not from the ceiling being too low.

Related

Get API access

Claude rate card