LLM API rate limits explained: RPM, TPM and what to do on a 429
Every model API meters requests and tokens per minute, and refills them continuously like a bucket, which is why a burst fails under the limit. How Anthropic, OpenAI, Amazon Bedrock and Azure OpenAI each count, the setting that makes Bedrock and Azure reject requests early, and retry code that waits the right amount.
Tecyfy · Published · 9 min read

A rate limit is the most an API lets one account send in a period, counted in requests per minute (RPM) and tokens per minute (TPM). Past it, the API answers HTTP 429 Too Many Requests and generates nothing. The fix is never just "send less": it is to read the retry-after header and wait that long, to spread requests out instead of firing them together, and on OpenAI, Azure and Bedrock to stop asking for a far larger max_tokens than you need.
The last of those surprises most teams. On some platforms the limit is checked against what you might use, not what you do use.
Why a burst fails under the limit
The limit is quoted per minute, but capacity is not handed out once a minute. It works like a bucket: each request takes from it, and it refills at a steady rate. Anthropic says so outright: a limit of 60 requests a minute may be enforced as 1 a second. Azure checks requests over 1 or 10-second windows, so 600 RPM is really about 10 a second.
Turn up the traffic and watch where requests start bouncing:
0 / 10 left
- accepted
- accepted
- accepted
- accepted
- accepted
- accepted
- accepted
- accepted
- accepted
- accepted
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
- 429
10 accepted20 × 429
20 of 30 requests get a 429, although 30 a minute is inside the 60 a minute limit. Sent in the same second, only what the bucket holds gets through. Spread the same requests over the minute and all of them pass.
A 60 requests a minute limit enforced as a bucket that refills 1 request a second, as Anthropic describes. The bucket size of 10 is an example; providers do not publish theirs.
So 30 requests fired in the same second can fail while the minute's total is well under 60. Running a fixed number of requests at a time, and ramping traffic up instead of switching it on, avoids most of these errors before any retry is needed.
What each platform counts
| Claude API | OpenAI | Amazon Bedrock | Azure OpenAI | |
|---|---|---|---|---|
| Request limit | RPM | RPM (and per day) | RPM | RPM, from the TPM quota |
| Token limit | Input and output separately | TPM (and per day) | TPM per model | TPM per deployment |
Does max_tokens count? | No, only tokens written | Yes, in the estimate | Yes, until the request ends | Yes, in the estimate |
| Cache reads count? | No, on most models | Yes | No | (not stated) |
| Where it is set | Usage tier, per model class | Usage tier, per org and project | Service quota, per region | Quota per subscription, region and model |
2 rows decide most designs:
max_tokens. OpenAI checks your request against the larger ofmax_tokensand its own estimate; Azure addsmax_tokensto its estimate; Bedrock deducts input plusmax_tokensthe moment the request starts and gives the difference back at the end. Bedrock's own example shows amax_tokensof 32,000 instead of 1,250 cutting the requests a quota allows sharply. On the Claude API it costs nothing against the limit. Set it close to what you need.- Bedrock's burndown. On Bedrock, output tokens of newer Claude models count several times against the quota: 5 times for Claude up to 4.7, 10 times for Opus 5.5, Sonnet 5 and Fable 5.1, and 15 times for Claude 4.8. A quota that looks generous for input runs out on long answers.
Caching helps beyond the bill: on the Claude API and Bedrock, input read from the prompt cache does not count toward the input limit, so caching a long shared prompt raises how much you can send. If your cache is not hitting, the prompt caching checklist finds out why.
Retry the right way
Every 429 from Anthropic carries a retry-after header; Azure sends retry-after-ms. Wait at least that long. Without a header, back off exponentially with random jitter so a crowd of clients does not retry in step:
import random, time
import anthropic
client = anthropic.Anthropic(max_retries=0)
def call(**kwargs):
for attempt in range(6):
try:
return client.messages.create(**kwargs)
except anthropic.RateLimitError as e:
if "enforced_spend_limit_reached" in str(e.body):
raise # a spending cap: retrying cannot help
wait = e.response.headers.get("retry-after")
time.sleep(float(wait) if wait else min(60, 2 ** attempt) + random.random())
raise RuntimeError("still rate limited after 6 tries")
The official SDKs already retry a 429 twice with backoff, so the simplest change is often max_retries=5 on a background job. Keep it low where a person is waiting: a quick "busy, try again" beats a minute of silence.
Not every 429 is worth retrying. When an Anthropic account hits its monthly spend cap, the 429 has no retry-after and an error_code of enforced_spend_limit_reached. It fails until someone raises the cap, so alert instead of looping.
See it coming
Every response says how much is left: the anthropic-ratelimit-* headers on Claude and the x-ratelimit-* headers on OpenAI and Azure. Log them beside your usage. A "remaining" line that falls toward 0 at the same hour each day tells you to cache, batch, move background work to a batch API, or ask for a higher tier before customers see errors. On Azure and Bedrock, quota is per region and per model, so a second region can double what you can send. Where capacity must be guaranteed, both sell reserved throughput.
For the idea in one page, see the rate limitRate LimitThe most an API lets you send in a period, counted in requests and tokens per minute. Past it, requests fail with a 429 error until capacity comes back, so code waits and retries. concept. To pick the cheapest model that fits your traffic, the cost calculator prices the same requests on each.
Questions
- What do RPM and TPM mean in an LLM API?
- RPM is requests per minute and TPM is tokens per minute, the 2 main limits on how much an account can send. Anthropic splits tokens into input tokens per minute (ITPM) and output tokens per minute (OTPM). Whichever limit runs out first stops you, and the API returns HTTP 429 until capacity comes back.
- Why do I get a 429 error when I am under the rate limit?
- Because capacity refills continuously rather than once a minute. Anthropic uses a token bucket, so a limit of 60 requests a minute can behave like 1 a second; Azure OpenAI checks requests over 1 or 10-second windows. A burst of requests in the same second can fail even though the minute's total is under the limit.
- Does max_tokens count against the rate limit?
- On OpenAI and Azure OpenAI, yes: the limit is checked against an estimate that includes max_tokens, so an oversized value rejects requests early. On Amazon Bedrock, input plus max_tokens is deducted at the start and corrected when the request ends. On the Claude API, no: output limits count only the tokens actually generated.
- How should I retry after a 429?
- Wait at least as long as the retry-after header says, then retry. If there is no header, use exponential backoff with random jitter so many clients do not retry together. The official SDKs already retry twice by default. Do not retry a 429 that means a spending cap was reached; it will keep failing until the cap is raised.
- Do cached tokens count toward the rate limit?
- On the Claude API, cache reads do not count toward input tokens per minute on most models, so caching raises your effective limit. On Amazon Bedrock, cache reads are also left out of the quota. On OpenAI, cached tokens still count toward tokens per minute.
Sources