Rate Limiting & Token Management
Master the art of managing API rate limits, optimizing token usage, and controlling costs when using Claude Code and Anthropic APIs.
Overview
Effective rate limit management and token optimization are crucial for building reliable, cost-effective applications with Claude Code. This guide covers everything from understanding rate limits to implementing advanced optimization strategies.
1. Understanding Rate Limits
API Usage Tiers (2025)
Free Tier
- Monthly Budget: Up to $10 of API usage
- Rate Limits:
- 5 requests per minute (RPM)
- 20,000 input tokens per minute (ITPM)
- 300,000 tokens per day
- Best For: Experimentation and small projects
Build Tiers
| Tier | Deposit | Wait Time | Monthly Usage | Rate Limits |
|---|---|---|---|---|
| Build 1 | $5 | None | Up to $100 | 50 RPM, 40K ITPM |
| Build 2 | $40 | 7 days | Up to $500 | 1,000 RPM, 400K ITPM |
| Build 3 | $200 | 7 days | Up to $1,000 | 2,000 RPM, 400K ITPM |
| Build 4 | $400 | 14 days | Up to $5,000 | 4,000 RPM, 800K ITPM |
Claude.ai Subscription Plans
- Free Plan: Variable daily cap based on demand
- Pro Plan ($20/month):
- At least 5x free usage
- ~45 messages every 5 hours
- ~10-40 Claude Code prompts every 5 hours
- Max Plan ($100/month):
- 5x Pro usage (~225 messages/5 hours)
- Max Plan ($200/month):
- 20x Pro usage (~900 messages/5 hours)
- Soft limit of 50 sessions/month
Understanding the 5-Hour Window
Claude.ai uses a rolling 5-hour window for rate limiting:
- Limits reset every 5 hours
- Usage is measured within each window
- Plan your intensive work around these windows
2. Token Counting and Measurement
Using the Token Count API
import anthropic
client = anthropic.Anthropic()
# Count tokens before sending
response = client.messages.count_tokens(
model="claude-3-5-sonnet-20241022",
messages=[
{"role": "user", "content": "Your prompt here"}
]
)
print(f"Input tokens: {response.input_tokens}")Token Counting Best Practices
- Pre-flight Checks: Count tokens before sending expensive requests
- Batch Analysis: Count tokens for multiple prompts to optimize batching
- Cost Estimation: Calculate costs before executing large operations
Getting Token Counts from Responses
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=1000
)
# Access usage data
print(f"Input tokens: {message.usage.input_tokens}")
print(f"Output tokens: {message.usage.output_tokens}")3. Rate Limit Handling Strategies
Client-Side Rate Limiting
Token Bucket Algorithm
import time
from collections import deque
class TokenBucket:
def __init__(self, capacity, refill_rate):
self.capacity = capacity
self.tokens = capacity
self.refill_rate = refill_rate
self.last_refill = time.time()
def consume(self, tokens=1):
self.refill()
if self.tokens >= tokens:
self.tokens -= tokens
return True
return False
def refill(self):
now = time.time()
elapsed = now - self.last_refill
self.tokens = min(self.capacity,
self.tokens + elapsed * self.refill_rate)
self.last_refill = nowExponential Backoff
import time
import random
def exponential_backoff(attempt, base_delay=1, max_delay=60):
"""Calculate exponential backoff with jitter"""
delay = min(base_delay * (2 ** attempt), max_delay)
jitter = random.uniform(0, delay * 0.1)
return delay + jitter
# Usage
for attempt in range(5):
try:
response = client.messages.create(...)
break
except anthropic.RateLimitError:
delay = exponential_backoff(attempt)
print(f"Rate limited. Waiting {delay:.2f}s...")
time.sleep(delay)Request Queue Management
import asyncio
from queue import PriorityQueue
class RequestQueue:
def __init__(self, rate_limiter):
self.queue = PriorityQueue()
self.rate_limiter = rate_limiter
async def add_request(self, priority, request_func):
await self.queue.put((-priority, request_func))
async def process_queue(self):
while True:
if not self.queue.empty() and self.rate_limiter.consume():
_, request_func = await self.queue.get()
try:
await request_func()
except Exception as e:
print(f"Request failed: {e}")
else:
await asyncio.sleep(0.1)4. Cost Optimization Techniques
Model Selection Strategy
| Model | Input Cost | Output Cost | Best Use Case |
|---|---|---|---|
| Claude 3 Haiku | $0.25/M | $1.25/M | Simple tasks, classification |
| Claude 3.5 Sonnet | $3/M | $15/M | Balanced performance/cost |
| Claude 4 Opus | $15/M | $75/M | Complex reasoning, analysis |
Prompt Caching
Prompt caching can reduce costs by 90%+ for repetitive prompts:
# Enable caching for frequently used prompts
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
messages=[
{
"role": "user",
"content": "Your system prompt here",
"cache_control": {"type": "ephemeral"}
}
],
max_tokens=1000
)Caching Benefits
- Cache writes: Slightly higher initial cost
- Cache reads: 3.00/M for input tokens
- Ideal for: System prompts, templates, repeated contexts
Batch Processing
Take advantage of 50% discount for non-urgent tasks:
# Use batch API for bulk operations
batch_response = client.batches.create(
requests=[
{
"custom_id": "req-1",
"params": {
"model": "claude-3-haiku-20240307",
"messages": [{"role": "user", "content": "Task 1"}]
}
},
# ... more requests
]
)Token-Efficient Prompting
- Concise Instructions: Use clear, brief prompts
- Structured Output: Request specific formats to reduce tokens
- Context Pruning: Remove unnecessary context between turns
- Smart Truncation: Intelligently truncate long inputs
5. Monitoring and Analytics
Built-in Claude Code Commands
# Check current usage
/cost
# Monitor session tokens
/status
# View rate limit status
/infoOpen Source Monitoring Tools
Claude Code Usage Monitor
Terminal-based real-time monitoring with:
- Token consumption tracking
- Burn rate calculations
- ML-based predictions
- Support for all plan types
# Install
npm install -g claude-code-usage-monitor
# Run
ccum --plan pro --dashboardPrometheus/Grafana Integration
# docker-compose.yml
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3000:3000"Custom Monitoring Implementation
import time
from dataclasses import dataclass
from typing import Dict, List
@dataclass
class UsageMetrics:
timestamp: float
input_tokens: int
output_tokens: int
model: str
cost: float
class UsageMonitor:
def __init__(self):
self.metrics: List[UsageMetrics] = []
def track_usage(self, response):
usage = response.usage
cost = self.calculate_cost(
usage.input_tokens,
usage.output_tokens,
response.model
)
metric = UsageMetrics(
timestamp=time.time(),
input_tokens=usage.input_tokens,
output_tokens=usage.output_tokens,
model=response.model,
cost=cost
)
self.metrics.append(metric)
def get_hourly_stats(self) -> Dict:
# Calculate hourly statistics
pass6. Error Handling and Recovery
Common Rate Limit Errors
429 Too Many Requests
try:
response = client.messages.create(...)
except anthropic.RateLimitError as e:
print(f"Rate limit hit: {e}")
# Implement backoff strategySession Limits
- Understand 5-hour reset windows
- Plan intensive work accordingly
- Use multiple accounts for critical operations
Robust Error Handling
import logging
from tenacity import retry, stop_after_attempt, wait_exponential
logger = logging.getLogger(__name__)
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=4, max=60)
)
async def resilient_api_call(client, **kwargs):
try:
return await client.messages.create(**kwargs)
except anthropic.RateLimitError as e:
logger.warning(f"Rate limit: {e}")
raise
except anthropic.APIConnectionError as e:
logger.error(f"Connection error: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise7. Advanced Optimization Strategies
Multi-Account Management
class MultiAccountManager:
def __init__(self, api_keys: List[str]):
self.clients = [
anthropic.Anthropic(api_key=key)
for key in api_keys
]
self.current_index = 0
def get_next_client(self):
client = self.clients[self.current_index]
self.current_index = (self.current_index + 1) % len(self.clients)
return clientIntelligent Request Routing
class IntelligentRouter:
def __init__(self):
self.model_costs = {
"haiku": {"input": 0.25, "output": 1.25},
"sonnet": {"input": 3, "output": 15},
"opus": {"input": 15, "output": 75}
}
def select_model(self, task_complexity, budget_remaining):
if task_complexity < 0.3 and budget_remaining > 10:
return "claude-3-haiku-20240307"
elif task_complexity < 0.7:
return "claude-3-5-sonnet-20241022"
else:
return "claude-4-opus-20250514"Context Window Management
class ContextManager:
def __init__(self, max_tokens=150000):
self.max_tokens = max_tokens
self.conversation_history = []
def add_turn(self, role, content, tokens):
self.conversation_history.append({
"role": role,
"content": content,
"tokens": tokens
})
self._prune_if_needed()
def _prune_if_needed(self):
total_tokens = sum(turn["tokens"] for turn in self.conversation_history)
while total_tokens > self.max_tokens and len(self.conversation_history) > 2:
removed = self.conversation_history.pop(0)
total_tokens -= removed["tokens"]Best Practices Summary
- Pre-flight Checks: Always count tokens before expensive operations
- Implement Client-Side Limiting: Don’t rely solely on server rate limits
- Use Appropriate Models: Match model complexity to task requirements
- Cache Aggressively: Use prompt caching for repeated contexts
- Monitor Continuously: Deploy comprehensive monitoring solutions
- Plan for Failures: Implement robust retry logic with backoff
- Optimize Context: Prune unnecessary conversation history
- Batch When Possible: Take advantage of batch processing discounts
Quick Reference
Environment Variables
# Rate limit configuration
export ANTHROPIC_RATE_LIMIT_STRATEGY="token_bucket"
export ANTHROPIC_MAX_RETRIES=5
export ANTHROPIC_BACKOFF_BASE=2
# Monitoring
export CLAUDE_USAGE_TRACKING=true
export CLAUDE_COST_ALERTS_THRESHOLD=100CLI Commands
# Check usage
claude /cost
claude /status
# Manage rate limits
claude /compact # Reduce context
claude /clear # Reset conversationSee Also
- Claude Code CLI Reference - CLI installation and usage
- TypeScript SDK - Programmatic rate limit handling
- Performance Optimization - General optimization strategies
- Monitoring Guide - Setting up comprehensive monitoring