返回指南
Troubleshooting·2026年7月17日·6 min read

Claude API 429 rate_limit_error — causes and the fix that holds

A 429 from the Claude API means you crossed one of Anthropic's per-minute limits — requests, input tokens, or output tokens. The fix is rarely "wait longer": it's honoring retry-after, adding jittered backoff, and smoothing bursts. Here is the complete playbook.

A 429 from the Claude API means you crossed one of Anthropic's per-minute limits — requests, input tokens, or output tokens. The fix is rarely "wait longer": it's honoring retry-after, adding jittered backoff, and smoothing bursts. Here is the complete playbook.

The error

response (HTTP 429)
{
  "type": "error",
  "error": {
    "type": "rate_limit_error",
    "message": "Number of request tokens has exceeded your per-minute rate limit"
  }
}

Causes and fixes at a glance

CauseFix
Requests-per-minute (RPM) limit hitQueue requests client-side; honor the retry-after header before retrying.
Input-tokens-per-minute (ITPM) hit — large prompts, few requestsTrim retrieved context, enable prompt caching so cached tokens stop counting against you on supported plans.
Output-tokens-per-minute (OTPM) hitSet max_tokens realistically — OTPM is often the first ceiling for long generations.
Burst traffic (cron fires everything at :00)Add jitter to schedules; spread batch jobs across the minute.

Read the response before retrying

Anthropic returns a retry-after header with the seconds to wait, and the error message names which limit you crossed. Retrying instantly without reading it is how a single 429 becomes a 429 storm.

Add exponential backoff with jitter

Retry only retryable statuses (429, 500, 529), never auth or validation errors. This snippet works unchanged against Anthropic directly or any OpenAI-compatible endpoint:

backoff.py
import time, random
from openai import OpenAI, APIStatusError

client = OpenAI(base_url="https://api.kunavo.com/v1", api_key="sk-kunavo-...")

def with_backoff(fn, max_retries=5):
    for attempt in range(max_retries):
        try:
            return fn()
        except APIStatusError as e:
            if e.status_code not in (429, 500, 529):
                raise                      # don't retry auth/validation errors
            retry_after = e.response.headers.get("retry-after")
            delay = float(retry_after) if retry_after else min(2 ** attempt, 30)
            time.sleep(delay + random.uniform(0, 0.5))   # jitter avoids herds
    raise RuntimeError("retries exhausted")

resp = with_backoff(lambda: client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "ping"}],
    max_tokens=32,
))
print(resp.choices[0].message.content)

Reduce the tokens, not just the requests

If the message says token limits, backoff alone won't save you. Cut retrieved chunks, cap max_tokens, and turn on prompt caching — a large stable system prompt at 10% of the input rate also relieves ITPM pressure.

If you're calling through Kunavo

Calling Claude through Kunavo doesn't magically remove rate limits, but it changes the failure economics: failed requests (including 429s) are never billed, and the per-key usage dashboard shows exactly which key and model is bursting so you can smooth it. The same backoff snippet above works as-is — just the base_url differs.

FAQ

Does upgrading my Anthropic tier remove 429s?

Higher tiers raise the per-minute ceilings, so 429s get rarer, but any fixed ceiling can be hit by a burst. Production code needs backoff regardless of tier.

Should I retry a 429 immediately?

No — honor the retry-after header (or use exponential backoff with jitter if it's absent). Immediate retries extend the rate-limited window and can escalate into a longer lockout.

Do failed 429 requests cost money?

Anthropic doesn't charge for rejected requests, and neither does Kunavo — failed requests are never billed. The cost of a 429 is latency, not dollars.

More error semantics live in the error reference; getting a key takes a minute via sign up and the authentication docs.