CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/API & SDK
API & SDK/2026-03-09Intermediate

Claude API Rate Limits — Designing Automation That Doesn't Stall on 429s

Understand how Claude API rate limits (RPM, TPM, TPD) work, and learn practical patterns — token optimization, request queuing, batch processing, and Retry-After-aware retries — drawn from running multiple sites on automation.

API27rate limits5token managementscalingoptimization4

Claude API Rate Limits — Designing Automation That Doesn't Stall

When you run several blogs on automated posting, you notice a strange thing: requests stall only in the moments when a batch goes out at night. Chase it down and the cause is almost never a bug in the code — it's rate limiting. If you're folding the Claude API into your own automation as an indie developer, the more you understand this behavior up front, the less you scramble later.

Here I walk through how RPM, TPM, and TPD work, how to save tokens, how to control request flow, and how to retry gracefully when you do hit a limit — in the order I picked them up while running things in production.

How Rate Limits Work

Rate limits ensure API stability and fair resource distribution across all users.

Types of Limits

Claude API applies rate limits on three axes:

Limit TypeDescriptionUnit
RPM (Requests Per Minute)Maximum requests per minuteRequest count
TPM (Tokens Per Minute)Maximum input + output tokens per minuteToken count
TPD (Tokens Per Day)Maximum input + output tokens per dayToken count

Plan-Based Limits

Rate limits vary by plan and model. Check Anthropic's documentation for the latest values. Higher-tier plans generally receive higher limits.

Using Response Headers

API responses include headers showing your current rate limit status. Use these to control request flow:

RateLimit-Limit-Requests: 50
RateLimit-Limit-Tokens: 90000
RateLimit-Remaining-Requests: 35
RateLimit-Remaining-Tokens: 72000
RateLimit-Reset-Requests: 2026-03-09T12:00:30Z
RateLimit-Reset-Tokens: 2026-03-09T12:00:30Z
Retry-After: 15

Optimizing Token Usage

Optimizing token consumption improves both cost efficiency and rate limit headroom.

1. Prompt Optimization

Write concise, effective prompts to reduce input tokens:

# Inefficient prompt (high token count)
inefficient_prompt = """
Please read the following text. Please analyze the content of the text.
Based on your analysis, please generate a summary of the text.
Please make the summary concise. Keep it as short as possible.
Avoid long summaries. Extract only the key points.
"""
 
# Optimized prompt (same intent, fewer tokens)
efficient_prompt = """
Summarize the following text in 3 sentences or fewer.
"""

2. Setting max_tokens Appropriately

Set max_tokens to the minimum needed for your use case:

import anthropic
 
client = anthropic.Anthropic()
 
# For short expected responses
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=256,  # Use a small value for short responses
    messages=[
        {"role": "user", "content": "Translate this word to Japanese: efficiency"}
    ]
)

3. Efficient System Prompts

System prompts are sent with every request, so brevity matters:

# Verbose system prompt
verbose_system = """
You are an excellent assistant. Please answer user questions politely.
Answers should be accurate, easy to understand, and of appropriate length.
Include examples when needed. If uncertain, communicate that clearly.
Understand the user's intent precisely and provide optimal answers.
"""
 
# Optimized system prompt
concise_system = "Accurate, concise technical assistant. State uncertainty when present."

4. Pre-Checking Token Counts

Estimate token counts before sending requests to avoid rate limit overruns:

import anthropic
 
client = anthropic.Anthropic()
 
# Use the token counting API
count_response = client.messages.count_tokens(
    model="claude-sonnet-4-6",
    messages=[
        {"role": "user", "content": "A long text..."}
    ],
    system="System prompt"
)
 
print(f"Input tokens: {count_response.input_tokens}")

Request Efficiency Techniques

1. Request Queuing

Queue requests with flow control to prevent hitting rate limits:

import asyncio
import time
from collections import deque
 
class RequestQueue:
    """Rate-limit-aware request queue"""
 
    def __init__(self, max_rpm: int = 50, max_tpm: int = 90000):
        self.max_rpm = max_rpm
        self.max_tpm = max_tpm
        self.request_times: deque = deque()
        self.token_usage: deque = deque()
        self.lock = asyncio.Lock()
 
    async def wait_if_needed(self, estimated_tokens: int):
        """Wait if approaching rate limits"""
        async with self.lock:
            now = time.time()
 
            # Remove entries older than 1 minute
            while self.request_times and now - self.request_times[0] > 60:
                self.request_times.popleft()
            while self.token_usage and now - self.token_usage[0][0] > 60:
                self.token_usage.popleft()
 
            # RPM check
            if len(self.request_times) >= self.max_rpm:
                wait_time = 60 - (now - self.request_times[0])
                if wait_time > 0:
                    print(f"RPM limit approaching. Waiting {wait_time:.1f}s")
                    await asyncio.sleep(wait_time)
 
            # TPM check
            current_tokens = sum(t[1] for t in self.token_usage)
            if current_tokens + estimated_tokens > self.max_tpm:
                wait_time = 60 - (now - self.token_usage[0][0])
                if wait_time > 0:
                    print(f"TPM limit approaching. Waiting {wait_time:.1f}s")
                    await asyncio.sleep(wait_time)
 
            self.request_times.append(time.time())
            self.token_usage.append((time.time(), estimated_tokens))

2. Using Batch Processing

For high-volume workloads, the Message Batches API helps avoid rate limit pressure:

import anthropic
 
client = anthropic.Anthropic()
 
# Create a batch request
batch = client.messages.batches.create(
    requests=[
        {
            "custom_id": f"request-{i}",
            "params": {
                "model": "claude-sonnet-4-6",
                "max_tokens": 1024,
                "messages": [
                    {"role": "user", "content": f"Task {i}: {task}"}
                ]
            }
        }
        for i, task in enumerate(tasks)
    ]
)
 
# Check batch status
result = client.messages.batches.retrieve(batch.id)
print(f"Status: {result.processing_status}")

Key benefits of batch processing:

  • Rate limits are managed separately from real-time requests
  • 50% cost discount
  • Efficient handling of large request volumes
  • Results delivered within 24 hours

3. Leveraging Prompt Caching

Use caching for repeated prompts to reduce token reprocessing:

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "Large context content...",
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[
        {"role": "user", "content": "Your question"}
    ]
)

Cache hits reduce input token costs by 90%.

When You Hit a Limit — Don't Ignore Retry-After

No matter how well you smooth the flow, on a shared platform the chance of hitting a limit never quite reaches zero. I once found my auto-posting script stalled on a 429 in the middle of the night and only noticed the next morning. What that taught me: rather than trying to avoid the error, it's sturdier to design what happens after you receive a 429.

When the Claude API returns a 429, it often tells you how long to wait via the Retry-After header. Honoring that value beats guessing a delay yourself. Only when it's absent should you back off exponentially — and add a little jitter so retries from multiple processes don't collide on the same instant.

import time, random
import anthropic
from anthropic import RateLimitError
 
client = anthropic.Anthropic()
 
def create_resilient(messages, max_retries=6):
    """Honor Retry-After on 429; otherwise back off with jitter."""
    backoff = 2.0
    for attempt in range(max_retries):
        try:
            return client.messages.create(
                model="claude-sonnet-4-6",
                max_tokens=1024,
                messages=messages,
            )
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise
            retry_after = None
            resp = getattr(e, "response", None)
            if resp is not None:
                hdr = resp.headers.get("retry-after")
                if hdr:
                    retry_after = float(hdr)
            wait = retry_after if retry_after is not None else backoff
            wait += random.uniform(0, 0.5)  # jitter spreads out concurrent retries
            time.sleep(wait)
            backoff *= 2
    raise RuntimeError("max retries exceeded")

The priority that matters here: follow the server when it tells you how long to wait, and decide for yourself only when it doesn't. A fixed delay falls short during congestion and wastes time when things are quiet. Since I moved Dolice's automation to this shape, the nightly jobs almost never die on an error.

Monitoring and Visibility

Usage Tracking

import anthropic
from datetime import datetime
 
class UsageTracker:
    """Track API usage metrics"""
 
    def __init__(self):
        self.requests = []
 
    def track(self, response):
        """Record usage from a response"""
        self.requests.append({
            "timestamp": datetime.now().isoformat(),
            "model": response.model,
            "input_tokens": response.usage.input_tokens,
            "output_tokens": response.usage.output_tokens,
            "cache_read_tokens": getattr(
                response.usage, "cache_read_input_tokens", 0
            ),
            "cache_creation_tokens": getattr(
                response.usage, "cache_creation_input_tokens", 0
            ),
        })
 
    def summary(self):
        """Print usage summary"""
        total_input = sum(r["input_tokens"] for r in self.requests)
        total_output = sum(r["output_tokens"] for r in self.requests)
        total_cache = sum(r["cache_read_tokens"] for r in self.requests)
 
        print(f"Total requests: {len(self.requests)}")
        print(f"Total input tokens: {total_input:,}")
        print(f"Total output tokens: {total_output:,}")
        print(f"Cache read tokens: {total_cache:,}")
 
# Usage
tracker = UsageTracker()
response = client.messages.create(...)
tracker.track(response)
tracker.summary()

Scaling Strategies

Gradual Scale-Up

For production deployments, increase traffic gradually:

  1. Development: Default rate limits are sufficient
  2. Testing: Consider plan upgrades if load tests hit limits
  3. Early production: Monitor real traffic and adjust
  4. Scale phase: Contact Anthropic for custom rate limits

Requesting Higher Limits

If default limits are insufficient, request increases through the Anthropic Console. Prepare the following information:

  • Current usage patterns (actual RPM, TPM values)
  • Required limits with justification
  • Use case description
  • Expected traffic growth rate

Where to Start

Rate limits look like an obstacle, but they're really traffic control that keeps a shared resource fair. Before reaching for a higher plan, there's something you can do first.

The step I'd recommend: add one piece of usage logging to the code you already run. Just recording each response's usage reveals whether your bottleneck is input tokens or request frequency. Once you know which axis is jammed, the fix picks itself — trim prompts, throttle with a queue, or move to batches. Measure, then move. It looks like a detour, but it's the fastest road I've found.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API & SDK2026-03-24
Claude API Usage & Cost API: Programmatic Cost Monitoring and Optimization
Learn how to use Anthropic's Usage & Cost Admin API to programmatically monitor, analyze, and optimize your Claude API spending with practical code examples.
API & SDK2026-07-14
A Two-Stage Pre-Publish Gate for User-Facing AI Text in Consumer Apps
Design a two-stage pre-publish gate for short AI-generated text you ship to end users: a deterministic rule layer plus a Claude classifier, with fail-closed handling, generation-time vetting, and a cost model. Full implementation code included.
API & SDK2026-07-12
A Long Non-Streaming Response Was Billed Twice Past the 10-Minute Wall: Redesigning the SDK's Default Timeout and Retries
The Anthropic SDK's default 10-minute timeout and two automatic retries can silently re-run a long non-streaming response and bill you twice. Here is how the trap works, and how to close it with streaming, explicit timeout/max_retries, and a small local ledger — with measured before/after numbers.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →