The product took weeks to build and the demo is tomorrow. You push to staging, start a load test, and thirty seconds later the logs fill up with HTTP 429 responses: the Claude API has decided you have had enough for this minute. Here is how Anthropic's rate limits are actually built, which levers you still have, and how to design a system that degrades gently instead of falling over.
We do carry Haiku: the catalog has claude-haiku-5.5 and claude-haiku-4.5, with prices on the models page. They run in the Premium channel and are not available in the Cheap Haiku channel.
How Claude API rate limits work
Anthropic enforces limits at three levels, and you have to keep all three in mind at once:
- Requests per minute (RPM): the number of API calls in a 60-second sliding window. It caps concurrency no matter how small your prompts are.
- Tokens per minute (TPM): the input + output tokens the API will process in a 60-second window. One very long prompt can use up your whole TPM budget in a single call.
- Tokens per day (TPD): a daily ceiling on total token throughput. Even inside RPM and TPM, a long enough sustained load can exhaust it.
Each model has its own limits. They are most generous for Haiku, which is the cheapest to serve. They are tightest for Opus, because every request burns a lot of compute. Sonnet sits in the middle. This is not a bug: it is how Anthropic signals which model to use for high-volume work.
Limits apply per API key and per model. A key sending 4,000 RPM to Sonnet and another key sending 4,000 RPM to Haiku are in independent buckets. Two services sharing one key and one model share one bucket.
The headers worth reading
Every Claude API response carries headers with the state of your limits:
anthropic-ratelimit-requests-limit: 4000
anthropic-ratelimit-requests-remaining: 3,842
anthropic-ratelimit-requests-reset: 2026-04-29T12:01:00Z
anthropic-ratelimit-tokens-limit: 400000
anthropic-ratelimit-tokens-remaining: 312,000
anthropic-ratelimit-tokens-reset: 2026-04-29T12:01:00Z
When you go over a limit, you get an HTTP 429 Too Many Requests with a retry-after header that says how many seconds to wait. The response body holds an error object with type: "rate_limit_error".
What an HTTP 429 error means
A 429 says the API heard you and wants you to slow down. The worst reaction is to retry immediately at full speed, which keeps you pinned against the limit. The best is to respect the retry-after header and apply exponential backoff.
This is what a 429 response looks like:
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "Number of request tokens has exceeded your per-model limit..."
}
}
Common causes of 429s in production:
- Burst traffic: a queue drains all at once after a deploy
- Large prompts: a single 100K-token context window eats your TPM
- No backoff: retry loops without pauses feed a storm of requests
- Shared keys: several services on one key with no coordination
Anthropic's tier system
Anthropic splits API access into tiers by usage history and spend, and each tier raises the rate limits. As of early 2026 the tiers look roughly like this:
| Tier | Monthly spend | Sonnet RPM | Sonnet TPM | Opus RPM | Opus TPM |
|---|---|---|---|---|---|
| Free | $0 | 50 | 40,000 | 5 | 10,000 |
| Build (T1) | $5+ | 1,000 | 80,000 | 100 | 40,000 |
| Build (T2) | $40+ | 2,000 | 160,000 | 200 | 80,000 |
| Scale (T3) | $200+ | 4,000 | 400,000 | 400 | 200,000 |
| Scale (T4) | $1,000+ | 8,000 | 800,000 | 1,000 | 400,000 |
| Enterprise | Custom | Custom | Custom | Custom | Custom |
What to take from the table:
- Going from Free to Build is a sharp jump: 20× on Sonnet RPM.
- The gap between T3 and T4 takes serious spend, but it doubles everything.
- Enterprise limits are negotiated with Anthropic's sales team, often
>80,000RPM on Sonnet. - Haiku limits are typically 2-4× the Sonnet limits at every tier.
The cold start problem
New API keys start at the bottom tier. If your product needs T3 limits on day one, confirm them with the provider before launch. Using codexkey does not guarantee a higher tier or extra capacity.
How to handle rate limits
1. Exponential backoff with jitter
The standard retry logic. On every 429, wait an exponentially growing time and add random jitter, so clients do not all retry at the same moment (the "thundering herd" effect).
Python version:
import anthropic
import time
import random
def call_with_backoff(
client: anthropic.Anthropic,
max_retries: int = 5,
base_delay: float = 1.0,
**kwargs,
) -> anthropic.types.Message:
for attempt in range(max_retries):
try:
return client.messages.create(**kwargs)
except anthropic.RateLimitError as e:
if attempt == max_retries - 1:
raise
# Respect retry-after header if present
retry_after = getattr(e, "response", None)
if retry_after and retry_after.headers.get("retry-after"):
delay = float(retry_after.headers["retry-after"])
else:
delay = base_delay * (2 ** attempt)
# Add jitter: 0–50% of delay
jitter = delay * random.uniform(0, 0.5)
time.sleep(delay + jitter)
raise RuntimeError("Unreachable")
TypeScript version:
import Anthropic from "@anthropic-ai/sdk";
async function callWithBackoff(
client: Anthropic,
params: Anthropic.MessageCreateParams,
maxRetries = 5,
baseDelay = 1000,
): Promise<Anthropic.Message> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await client.messages.create(params);
} catch (err) {
if (
err instanceof Anthropic.RateLimitError &&
attempt < maxRetries - 1
) {
const retryAfter = err.headers?.["retry-after"];
const delay = retryAfter
? parseFloat(retryAfter) * 1000
: baseDelay * 2 ** attempt;
const jitter = delay * Math.random() * 0.5;
await new Promise((r) => setTimeout(r, delay + jitter));
continue;
}
throw err;
}
}
throw new Error("Unreachable");
}
2. Request queuing
Instead of sending requests at full speed, put them in a queue and drain it at a set rate. This matters most with bursty traffic: a user uploads 50 documents and every one has to be processed.
import asyncio
from collections import deque
class RateLimitedQueue:
def __init__(self, rpm: int = 1000):
self.interval = 60.0 / rpm
self.queue: deque = deque()
self.semaphore = asyncio.Semaphore(rpm // 10) # concurrency cap
async def submit(self, coro):
await self.semaphore.acquire()
try:
result = await coro
await asyncio.sleep(self.interval)
return result
finally:
self.semaphore.release()
3. Batching with the Batch API
If your workload can wait up to 24 hours, the Anthropic Batch API processes requests at half the cost and with separate rate limits. It suits classification jobs, data extraction, bulk summarisation and nightly pipelines.
import anthropic
client = anthropic.Anthropic()
batch = client.messages.batches.create(
requests=[
{
"custom_id": f"doc-{i}",
"params": {
"model": "claude-sonnet-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": f"Summarise: {doc}"}
],
},
}
for i, doc in enumerate(documents)
]
)
# Poll batch.id for results — up to 24h
Batching remains one of the most underused features of the Claude API. If even 30% of your traffic is not latency-sensitive, moving it to batches frees real-time RPM and TPM for the requests that need them.
Model routing: the biggest lever
Not every request needs Opus. Most do not even need Sonnet. A well-designed model router can cut costs by 60-80% and noticeably ease rate limit pressure by spreading load across models with different limit pools.
The principle is simple:
- gpt-5.6-luna: classification, extraction, simple Q&A, validation, routing decisions. Fast and cheap for this class of task.
- Sonnet: summarisation, code generation, multi-step reasoning, content creation. The workhorse.
- Opus: complex analysis, long-document reasoning, agentic loops, tasks where accuracy is critical and cost is secondary.
A production-grade router:
import anthropic
from enum import Enum
class Complexity(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
def estimate_complexity(prompt: str, token_count: int) -> Complexity:
"""Route based on heuristics. Replace with a classifier for precision."""
# Short prompts with clear instructions → gpt-5.6-luna
if token_count < 2000 and not any(
kw in prompt.lower()
for kw in ["analyze", "compare", "reason step by step", "architect"]
):
return Complexity.LOW
# Very long context or explicit reasoning demands → Opus
if token_count > 50_000 or "think carefully" in prompt.lower():
return Complexity.HIGH
return Complexity.MEDIUM
MODEL_MAP = {
Complexity.LOW: "gpt-5.6-luna",
Complexity.MEDIUM: "claude-sonnet-5",
Complexity.HIGH: "claude-opus-4.8",
}
def routed_call(client: anthropic.Anthropic, prompt: str, **kwargs):
token_est = len(prompt) // 4 # rough estimate
complexity = estimate_complexity(prompt, token_est)
model = MODEL_MAP[complexity]
return client.messages.create(
model=model,
messages=[{"role": "user", "content": prompt}],
**kwargs,
)
TypeScript version:
import Anthropic from "@anthropic-ai/sdk";
type Complexity = "low" | "medium" | "high";
function estimateComplexity(prompt: string): Complexity {
const len = prompt.length / 4;
const lowerPrompt = prompt.toLowerCase();
if (
len < 2000 &&
!["analyze", "compare", "reason step by step"].some((kw) =>
lowerPrompt.includes(kw),
)
) {
return "low";
}
if (len > 50_000 || lowerPrompt.includes("think carefully")) {
return "high";
}
return "medium";
}
const MODEL_MAP: Record<Complexity, string> = {
low: "gpt-5.6-luna",
medium: "claude-sonnet-5",
high: "claude-opus-4.8",
};
async function routedCall(
client: Anthropic,
prompt: string,
maxTokens = 4096,
): Promise<Anthropic.Message> {
const complexity = estimateComplexity(prompt);
return client.messages.create({
model: MODEL_MAP[complexity],
max_tokens: maxTokens,
messages: [{ role: "user", content: prompt }],
});
}
The main takeaway: routing saves money and also redistributes rate limit pressure. Send 60% of your traffic to gpt-5.6-luna and Sonnet capacity stays free for the requests that really need it.
Prompt caching: cutting token consumption
Prompt caching lets Anthropic skip re-processing the same input tokens across requests. If your system prompt, tool definitions and few-shot examples are identical from call to call, you can mark them cacheable and pay 10× less for those tokens on later requests.
The effect on rate limits is indirect but real: cached tokens count toward TPM at a reduced weight, so more requests fit into the same window.
import anthropic
client = anthropic.Anthropic()
# The system prompt and tools are cached after the first call
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": "You are a customer support agent for Acme Corp...",
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": user_query}],
)
# Check cache performance
print(f"Cache read: {response.usage.cache_read_input_tokens}")
print(f"Cache creation: {response.usage.cache_creation_input_tokens}")
Take a 6,000-token system prompt called 10,000 times a month. Caching cuts the effective input token spend from 60M tokens at $0.33/M ($19.80) to 60M tokens at $0.05/M ($3). That is a 90% reduction on the cached portion.
Using a gateway with explicit channel selection
A gateway changes the endpoint and account your client talks to. A codexkey key selects Premium or Cheap, optionally allowing Premium fallback for Cheap under the provider's policy. The dashboard key API does not expose per-key quota settings and does not promise a larger request pool. Client queues, bounded retries and workload measurements are still necessary.
Here is what changes in your code:
import anthropic
# Before: direct to Anthropic
client = anthropic.Anthropic(api_key="sk-ant-...")
# After: through codexkey — same SDK, different base URL
client = anthropic.Anthropic(
api_key="your-codexkey-key",
base_url="https://api.codexkey.online/v1",
)
# Everything else stays identical
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: "your-codexkey-key",
baseURL: "https://api.codexkey.online/v1",
});
// Compatible SDK, explicitly selected key channel
const response = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});
Check channel availability and current customer prices in the codexkey catalog before using a model. Gateway compatibility does not guarantee higher limits or a provider enterprise tier.
Rate limits across providers: comparison
| Factor | Anthropic T1 | Anthropic T3 | Anthropic Enterprise | codexkey | OpenAI (GPT-4o) | Together.ai |
|---|---|---|---|---|---|---|
| Sonnet/equiv. RPM | 1,000 | 4,000 | Custom | Not exposed by key API | 5,000 (GPT-4o) | 600 |
| Sonnet/equiv. TPM | 80,000 | 400,000 | Custom | Not exposed by key API | 800,000 | 100,000 |
| Opus/equiv. RPM | 100 | 400 | Custom | Not exposed by key API | 500 (o1) | N/A |
| Batch API | Yes (50% off) | Yes (50% off) | Yes (50% off) | Not confirmed | Yes (50% off) | No |
| Prompt caching | Yes (10× saving) | Yes (10× saving) | Yes (10× saving) | Yes (passthrough) | Yes (partial) | No |
| Multi-key pooling | Manual | Manual | Managed | Not guaranteed | Manual | Manual |
| Time to T3+ limits | Weeks | Weeks | Negotiation | Not guaranteed | Weeks | N/A |
| SDK compatibility | Native | Native | Native | Drop-in | Separate SDK | OpenAI-compatible |
Monitoring and alerting
Do not wait for 429s to learn you are close to a limit. Build monitoring on the rate limit headers:
import anthropic
import logging
logger = logging.getLogger("rate_limits")
def monitored_call(client: anthropic.Anthropic, **kwargs):
response = client.messages.create(**kwargs)
# Extract rate limit headers from the raw response
remaining_requests = response._response.headers.get(
"anthropic-ratelimit-requests-remaining"
)
remaining_tokens = response._response.headers.get(
"anthropic-ratelimit-tokens-remaining"
)
if remaining_requests and int(remaining_requests) < 100:
logger.warning(f"RPM running low: {remaining_requests} remaining")
if remaining_tokens and int(remaining_tokens) < 50_000:
logger.warning(f"TPM running low: {remaining_tokens} remaining")
return response
Set alerts for when remaining capacity drops below 20% of your limit. That leaves time to throttle, re-route or scale before the 429s start.
Putting it together: a production architecture
A solid production setup combines everything above:
- Model router classifies incoming requests and picks gpt-5.6-luna, Sonnet or Opus.
- Request queue sets a maximum drain rate per model.
- Prompt caching cuts token consumption for repeated system prompts.
- Retry with backoff absorbs transient 429s.
- Gateway (codexkey) supplies the channel selected for the key; verify provider policy and model availability before sending traffic.
- Monitoring warns you before you hit the wall, not after.
- Batch API moves non-urgent work to a separate limit pool at a 50% discount.
This is not over-engineering. Every production Claude deployment of meaningful scale runs into rate limits sooner or later. The only question is whether your system absorbs it quietly or someone gets woken up at 3 AM.
FAQ
What is a rate limit on the Claude API?
It is a cap on the requests (RPM), tokens (TPM) or daily tokens (TPD) your API key can use in a given window. Go over any of them and the API returns HTTP 429 until the window resets.
How do I check my rate limit tier?
Open the Anthropic Console and go to your API key settings: the tier is shown next to your usage. The anthropic-ratelimit-* headers in any API response also show your current limits and remaining capacity.
Can I ask Anthropic to raise my rate limits?
Yes. On the Build and Scale tiers, limits rise automatically as monthly spend grows. Custom limits beyond T4 are negotiated with Anthropic's sales team under an Enterprise agreement, which typically requires a minimum annual commitment.
What is the difference between RPM and TPM?
RPM (requests per minute) limits how many API calls you can make, whatever their size. TPM (tokens per minute) limits total token throughput. One very long prompt can hit TPM even when RPM is fine. Both ceilings apply at once.
Does the Batch API have separate rate limits?
Yes. Batch requests run asynchronously with their own limit pool and cost 50% less. They fit any workload that tolerates up to 24 hours of latency: classification pipelines, nightly reports, bulk data extraction.
How does prompt caching help with rate limits?
Cached tokens are processed faster and count at reduced weight against TPM. When a 6,000-token system prompt is cached, the API only processes the dynamic part of each request, which effectively raises your throughput inside the same TPM window.
Does using a gateway like codexkey violate Anthropic's terms?
Check the terms that apply to your account and provider agreement. codexkey key settings do not establish extra quota rights or request capacity, and gateway compatibility does not imply them.
What is the fastest way to stop getting 429 errors?
Start with the error itself and any retry guidance the provider returns. For retryable failures, add a bounded client queue and exponential backoff with jitter. Switching gateways does not guarantee higher limits.
Rate limits come with any managed API, and Claude is no exception. They are still a solvable problem. The teams that cope best plan for them before release, not after the first outage. codexkey provides compatible endpoints and channel selection. Check current customer prices and provider policy: additional quota or pooled capacity is not guaranteed.