← Blog

Claude API Rate Limit Handling Strategies That Work

2026-09-24 · 5 min read · SubToAPI Team

Claude API rate limits are enforced per organization across requests-per-minute (RPM), input tokens-per-minute (ITPM), and output tokens-per-minute (OTPM). When you exceed any of these, you get a 429 response with a retry-after header, and if your app doesn't handle that gracefully, users see failed requests instead of slightly slower ones.

The core strategies that actually solve this in production are: exponential backoff with jitter, client-side request queuing that respects token budgets (not just request counts), reading the rate limit headers Anthropic returns on every response, and — for teams that outgrow a single set of limits — distributing load across multiple API keys or accounts. Below is how to implement each one.

Read the Rate Limit Headers First

Before writing retry logic, use the information the API already gives you. Every response includes headers like:

anthropic-ratelimit-requests-limit: 50
anthropic-ratelimit-requests-remaining: 12
anthropic-ratelimit-requests-reset: 2024-01-15T10:32:00Z
anthropic-ratelimit-tokens-remaining: 8000
anthropic-ratelimit-tokens-reset: 2024-01-15T10:32:00Z

Parse these on every response, not just on 429s. If tokens-remaining is dropping fast relative to reset, throttle proactively instead of waiting for a hard failure. This turns rate limiting from a reactive problem into something you can smooth out before it happens.

function trackRateLimit(headers) {
  return {
    requestsRemaining: parseInt(headers.get('anthropic-ratelimit-requests-remaining')),
    tokensRemaining: parseInt(headers.get('anthropic-ratelimit-tokens-remaining')),
    resetAt: new Date(headers.get('anthropic-ratelimit-requests-reset')),
  };
}

Exponential Backoff with Jitter

When you do hit a 429, don't retry immediately — that just adds to the congestion. Use exponential backoff with random jitter so concurrent requests don't all retry at the same instant:

async function callWithBackoff(fn, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (err.status !== 429 || attempt === maxRetries - 1) throw err;

      const retryAfter = err.headers?.get('retry-after');
      const base = retryAfter ? parseInt(retryAfter) * 1000 : 2 ** attempt * 1000;
      const jitter = Math.random() * 500;
      await new Promise(r => setTimeout(r, base + jitter));
    }
  }
}

Always prefer the retry-after header value when present — it's more accurate than a guessed backoff curve, especially when the limit reset is only a few seconds away.

Queue Requests by Token Budget, Not Just Count

RPM limits are usually generous compared to token limits. A single request with a long system prompt or large context window can consume a big chunk of your per-minute token budget even if you're nowhere near your request count limit. A queue that only throttles on request count will still get 429s from token limits.

Build a simple token-aware queue:

class TokenBudgetQueue {
  constructor(tokensPerMinute) {
    this.limit = tokensPerMinute;
    this.used = 0;
    this.windowStart = Date.now();
  }

  async acquire(estimatedTokens) {
    const elapsed = Date.now() - this.windowStart;
    if (elapsed > 60000) {
      this.used = 0;
      this.windowStart = Date.now();
    }
    if (this.used + estimatedTokens > this.limit) {
      const wait = 60000 - elapsed;
      await new Promise(r => setTimeout(r, wait));
      this.used = 0;
      this.windowStart = Date.now();
    }
    this.used += estimatedTokens;
  }
}

Estimate tokens roughly (character count / 4 is a common heuristic) before the call, then reconcile with the actual usage reported in the response.

Prioritize and Shed Load Gracefully

Not all requests are equal. A user-facing chat completion is more time-sensitive than a background summarization job. When you're near your limit, prioritize interactive requests and delay or batch non-interactive ones:

This keeps your app responsive for the requests users are actually waiting on, instead of treating every request as equally urgent.

Cache and Deduplicate Aggressively

Rate limits only bite when you're actually sending requests. Two cheap wins reduce volume before you even need backoff logic:

Both reduce your effective request rate without any change to how Claude itself handles limits.

Scale Beyond a Single Key

If you've implemented backoff, token budgeting, and caching and you're still bumping into limits during peak traffic, the next step is distributing load. This usually means either requesting a limit increase from Anthropic for high-volume production use, or splitting traffic across multiple API keys tied to different accounts or seats.

This is where a layer like SubToAPI is useful if you're already on a Claude subscription rather than a metered API account — it exposes your access as a standard HTTPS API with application-level keys (sub_live_...), so you can issue separate keys per service or environment and manage usage from one dashboard instead of juggling credentials manually. Setup takes a few minutes; see the quickstart or pricing for the seat-based plans if you're distributing usage across a team.

Putting It Together

A production-grade rate limit handling stack usually looks like:

  1. Read and log rate limit headers on every response.
  2. Queue requests against a token budget, not just a request counter.
  3. Retry 429s with exponential backoff + jitter, honoring retry-after.
  4. Prioritize interactive traffic over background jobs.
  5. Cache and deduplicate to reduce raw request volume.
  6. Distribute across keys or request higher limits once you've optimized the above.

Most teams only need steps 1–3 to eliminate visible failures. Steps 4–6 matter once you're running Claude in production at meaningful scale.

FAQ

What's the difference between RPM and token-per-minute limits? RPM caps how many requests you can send per minute regardless of size. ITPM/OTPM cap the total input/output tokens processed per minute. A few large requests can hit token limits well before you hit the request count limit.

Should I retry every 429 automatically? Yes, but with backoff and a retry cap (3-5 attempts is typical). Retrying instantly or indefinitely can worsen congestion and won't resolve a limit that resets on a fixed schedule.

Can multiple API keys bypass rate limits? Multiple keys under the same organization typically share the same limits. To meaningfully distribute load, you need separate accounts, a higher limit tier from Anthropic, or a management layer that issues distinct keys per environment, like SubToAPI.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →