Claude API

Handling Claude API errors: 429, 529, spend caps and rate limits

TL;DR

Not every error should be retried. A 429 can mean rate limiting (with retry-after) or a spend cap (no retry until next month). Prompt caching raises effective throughput because cache reads don't count toward ITPM on most models.

An app calling the Claude API runs fine on a dev machine, then in production it occasionally gets a 429, sometimes a 529, and one day the whole system is "dead" until the start of next month and nobody understands why. Most of these incidents come from treating every error the same: retry everything, or retry nothing. This post organises the error codes, how rate limits work and the correct handling per Anthropic's official docs.

Error codes: which to retry, which not

HTTPerror.typeCauseRetry?
400invalid_request_errorMalformed request or content; also used when you hit a spend limit you set yourselfNo, fix the request
401authentication_errorWrong, revoked or expired API keyNo
402billing_errorBilling problemNo
403permission_errorKey lacks permission for the resourceNo
404not_found_errorWrong endpoint or IDNo
413request_too_largeExceeds size limits (Messages 32 MB, Batch 256 MB, Files 500 MB)No, split it
429rate_limit_errorRate limit exceeded, or tier spend cap reachedYes, per retry-after (except spend cap)
500api_errorInternal errorYes, exponential backoff
504timeout_errorProcessing took too longYes; consider switching to streaming
529overloaded_errorThe API is overloaded generally, not your faultYes, backoff

Errors always return JSON with error.type, error.message and request_id. Every response also has a request-id header: always log it, it's the first thing support asks for.

How rate limits work

  • Three limits per model: RPM (requests/minute), ITPM (input tokens/minute), OTPM (output tokens/minute). Exceeding any one returns a 429.
  • Token bucket: capacity refills continuously rather than resetting on the minute. 60 RPM may be enforced as 1 request/second, so short bursts can hit 429 even if the per-minute total isn't exceeded.
  • By tier: organisations are placed in tiers automatically based on usage history. New organisations may start in an Evaluation tier with lower limits.
  • Acceleration limits: sudden traffic spikes can get 429s even below the nominal limit. Ramp up gradually.

Prompt caching = a free limit increase

On most models, cache_read_input_tokens don't count toward ITPM; only input_tokens and cache_creation_input_tokens do. The docs give an example: with a 2M ITPM limit and an 80% cache hit rate, you can effectively process about 10M input tokens per minute. OTPM only counts tokens actually generated; a high max_tokens doesn't drain the limit.

The big trap: 429 from the spend cap

Each tier has a monthly spending cap. When you hit it, the API returns a 429 with the same error.type as a rate limit, but:

  • There's no retry-after header, and error.details.error_code is enforced_spend_limit_reached.
  • Retrying is pointless until 00:00 UTC on the 1st of next month, unless you move up a tier.

If your code only looks at the 429 status and retries forever, you burn CPU and logs while users wait indefinitely. Distinguish by error_code and alert immediately.

Suggested handling logic

try:
    resp = client.messages.create(...)
except anthropic.RateLimitError as e:
    body = e.body or {}
    code = body.get("error", {}).get("details", {}).get("error_code")
    if code == "enforced_spend_limit_reached":
        alert_oncall("Spend cap reached"); raise   # don't retry
    raise                                         # let the SDK/queue retry
except (anthropic.APIStatusError) as e:
    log.error("claude_error", status=e.status_code,
              request_id=e.response.headers.get("request-id"))
    raise

The official SDKs already retry twice automatically with exponential backoff for connection errors, 429s and 5xx, honouring retry-after. Tune with max_retries. Don't wrap an unbounded manual retry loop around it, or you'll multiply the number of attempts.

Monitor with headers

Every response has anthropic-ratelimit-requests-remaining, anthropic-ratelimit-input-tokens-remaining, anthropic-ratelimit-output-tokens-remaining and the matching -reset headers (RFC 3339 format). Record them in metrics to see when you're about to hit the ceiling.

How to apply it

  • Dev: Catch errors by SDK class, not by comparing message strings. Log request-id. Use streaming for long requests instead of raising timeouts.
  • Tech Lead: Route high-volume jobs through a queue with bounded concurrency; move non-urgent jobs to the Batch API. Enable prompt caching for system prompts and repeated documents to raise throughput.
  • PM: Set a spend limit per workspace in the Console and alert at 80% so you don't get "switched off" mid-month.

Sources & references

  1. Claude API errors — Claude Docs
  2. Rate limits — Claude Docs