CLAUDE LABEN
FORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられていますFORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられています
記事一覧/API & SDK
API & SDK/2026-05-01中級

Claude API のリトライを書いたらレート制限が悪化した話 — Retry-After を読まないリトライの罠

Claude API でリトライを実装したのに 429 エラーがむしろ増えた、という相談をよく受けます。原因のほとんどは Retry-After ヘッダーの無視と、ジッターのない指数バックオフです。診断と修正手順をまとめました。

claude-api81retry6rate-limit7backofftroubleshooting61anthropic12

「リトライを入れたのに、なぜか 429 が増えた気がする」— Claude API を本番投入した方から、これを一番よく相談されます。私自身も、初めて運用していた API クライアントに「3 秒待って 3 回まで再送」というシンプルな実装を入れたとき、ピーク時間帯にレート制限ヒットがかえって増えた経験があります。

直感的には、リトライは「優しい挙動」であるはずです。失敗したら少し待って再送するだけなのに、なぜ事態が悪くなるのでしょうか。理由は単純で、Retry-After ヘッダーを読まずに固定間隔で再送するクライアントが複数走ると、サーバーから見れば「同期した波」になるからです。Anthropic のドキュメントでは「指数バックオフを推奨」とだけ書かれていますが、実際に効くのは「ジッター付き指数バックオフ + Retry-After 優先」の組み合わせです。

ここでは私が個人開発で複数のアプリに Claude API を組み込む過程で何度も踏んだ「リトライの罠」を、診断順に整理します。コード例はすべて Node.js(公式 SDK 不使用の最小構成)で示しますが、Python やその他の言語でも考え方はそのまま使えます。

まずは usage と response headers を見る

リトライ戦略の見直しは、コードを書く前にサーバーから何が返っているかを確認するところから始めます。429 が返ってきたとき、レスポンスには次のヘッダーが含まれていることがあります。

  • retry-after: 再試行までに最低限待つべき秒数(数値、または HTTP 日付)
  • anthropic-ratelimit-requests-remaining: 残りリクエスト数
  • anthropic-ratelimit-requests-reset: 残数がリセットされる UNIX 時刻
  • anthropic-ratelimit-tokens-remaining: 残りトークン数
  • anthropic-ratelimit-tokens-reset: トークン枠がリセットされる UNIX 時刻

最初にやるべき診断は、429 を受け取ったときにこれらの値を全てログに出すことです。私は最初これを見ていなかったので、「なんとなく混雑しているらしい」程度の認識でした。実際にログを取り始めると、ある特定のリクエストでだけトークン枠を一気に使い切っていることに気づきました。原因はキャッシュ無効な巨大プロンプトを並列送信していたことで、リトライ戦略の問題というより設計の問題でした。

// 失敗時に必ずヘッダーをログに出す(最低限のフィールドだけ抜粋)
async function callClaude(body) {
  const res = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": process.env.ANTHROPIC_API_KEY,
      "anthropic-version": "2023-06-01",
      "content-type": "application/json",
    },
    body: JSON.stringify(body),
  });
 
  if (!res.ok) {
    // ★ 429 / 529 の時は必ずヘッダーを保存して原因分析に使う
    console.error({
      status: res.status,
      retryAfter: res.headers.get("retry-after"),
      reqRemaining: res.headers.get("anthropic-ratelimit-requests-remaining"),
      reqReset: res.headers.get("anthropic-ratelimit-requests-reset"),
      tokRemaining: res.headers.get("anthropic-ratelimit-tokens-remaining"),
      tokReset: res.headers.get("anthropic-ratelimit-tokens-reset"),
      requestId: res.headers.get("request-id"),
    });
  }
  return res;
}

request-id も忘れずに記録してください。Anthropic サポートに問い合わせる必要が出たとき、これが無いと調査が進みません。

ありがちな悪手 1: 固定秒数で待つだけのリトライ

最初に書きがちなのが、こういうコードです。

// ❌ 悪い例: 何度失敗しても同じ間隔で再送する
async function retryFixed(body, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const res = await callClaude(body);
    if (res.ok) return res;
    if (res.status === 429 || res.status === 529) {
      await new Promise((r) => setTimeout(r, 3000)); // 常に 3 秒
      continue;
    }
    return res;
  }
  throw new Error("max retries exceeded");
}

これが本当に良くない理由は、複数のクライアントが同時に 429 を受けると、ちょうど 3 秒後に全員が同時に再送する点にあります。サーバー側から見れば「同期したスパイク」が等間隔で繰り返し来る状態になります。これがいわゆる thundering herd(雷のような群衆)問題で、リトライを書いたつもりが攻撃に近い波形を生み出すことになります。

ありがちな悪手 2: ジッターなしの指数バックオフ

「指数バックオフ」と聞いて、こう書く人も多いです。

// ❌ 微妙な例: ジッター無し
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s...
await new Promise((r) => setTimeout(r, delay));

固定秒よりはマシですが、全クライアントの待機時間が同じである以上、波形は同期したままです。Anthropic 側から見ると「皆が 1 秒後に押し寄せ → 失敗 → 皆が 2 秒後に押し寄せ → 失敗」という綺麗な階段状のスパイクが残ります。

私の経験上、ここを直すだけで 429 のピーク発生率が体感半分以下になりました。

正しい実装: Retry-After 優先 + Full Jitter 指数バックオフ

AWS Architecture Blog で紹介されている "Full Jitter" バックオフは、Claude API でも非常に有効です。考え方はシンプルで、待機時間の上限を指数的に増やしつつ、その範囲でランダムに選ぶだけです。

// ✅ 推奨: Retry-After を最優先 + Full Jitter
function computeBackoffMs(attempt, retryAfter) {
  // 1) サーバーが Retry-After を返したら最優先で従う
  if (retryAfter) {
    const seconds = Number(retryAfter);
    if (!Number.isNaN(seconds)) return seconds * 1000;
    // HTTP 日付形式の場合
    const dateMs = Date.parse(retryAfter);
    if (!Number.isNaN(dateMs)) return Math.max(0, dateMs - Date.now());
  }
  // 2) ヘッダー無しなら Full Jitter 指数バックオフ
  const cap = 60_000; // 上限 60 秒
  const base = 500;   // 初期 0.5 秒
  const exp = Math.min(cap, base * 2 ** attempt);
  return Math.floor(Math.random() * exp); // 0〜exp の範囲でランダム
}
 
async function retryWithBackoff(body, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await callClaude(body);
    if (res.ok) return res;
 
    // 5xx と 429 のみリトライ。4xx 系(401, 400 等)は即時失敗
    const retriable = res.status === 429 || res.status === 529 || (res.status >= 500 && res.status < 600);
    if (!retriable) return res;
 
    const wait = computeBackoffMs(attempt, res.headers.get("retry-after"));
    console.warn(`[retry ${attempt + 1}/${maxRetries}] status=${res.status} waiting=${wait}ms`);
    await new Promise((r) => setTimeout(r, wait));
  }
  throw new Error("max retries exceeded");
}

期待する挙動は次の通りです。サーバーが「3 秒待って」と言えばその通りに従い、何も指示が無ければ 0〜0.5s → 0〜1s → 0〜2s → 0〜4s … と、各クライアントが独立した時間で再送するため、波形が散らばります。

見落としやすい注意点

実装を直してもまだ 429 が減らない場合、以下を順に疑ってください。

  1. 冪等性の欠如: 同じリクエストを 2 回送ると副作用が出る処理(DB 書き込み等)に Claude 応答を直結している場合、リトライがバグになります。message.id を使って重複検知するか、リクエスト側で UUID を発行して冪等キーを管理してください
  2. 並列度が上限を超えている: アカウント Tier ごとに同時リクエスト数の暗黙的な上限があります。バックオフを正しくしてもプール全体が制限を超えていれば、何度直しても改善しません。p-limit などで明示的に並列数を絞るのが最短です
  3. レスポンス未読のまま中断している: HTTP 接続を中断するとサーバー側ではリクエストが完了扱いされないことがあり、レート枠が無駄に消費されます。タイムアウトは AbortController で明示的に行い、応答がエラーでも res.body を読み切るのが安全です
  4. トークン枠の枯渇を 429 と勘違いしている: anthropic-ratelimit-tokens-remaining がゼロなら、リクエスト数を絞っても解決しません。プロンプトキャッシュ導入や入力長削減が効きます。プロンプトキャッシュの実装パターンは Claude API のプロンプトキャッシュがヒットしない時の診断手順 にまとめました

ストリーミング応答との組み合わせ

ストリーミングを使っている場合、リトライ判断は「最初の message_start イベントが返ってきたかどうか」で分岐します。ストリームの途中で切れたものを最初からやり直すと、トークンが二重に課金される上にレート枠も二度消費されます。途中切断時の安全な扱いは Claude API ストリーミングを本番で落とさない実装 で詳しくまとめていますので、ストリーミング派の方はそちらと合わせて読んでみてください。

リトライの設計を本格的に

新規プロジェクトに毎回入れている小さな共通モジュール

私は新しい Claude API 連携を始めるとき、必ず同じヘルパーモジュールを最初に入れています。中身は fetch のラッパーで、ここで紹介したリトライ戦略を実装し、concurrency オプションで内部的に並列数を絞り、構造化ログを他のテレメトリと同じ場所に流すだけです。だいたい 80 行程度の小さなコードですが、価値は「最初にデプロイした時点で 429 をまともに扱える」ことです。本番のトラフィックが来てから教訓を学び直す必要がなくなります。複数の Claude 連携を運用しているなら、この小さな抽象化を一度作ってしまうのがおすすめです。

次にやるべき一歩

まず、いま動いている Claude API クライアントの 429 ハンドラーを開いて、retry-after ヘッダーを読んでいるかだけ確認してください。読んでいなければ、上記の computeBackoffMs をコピーして組み込むのが最短です。それだけでピーク帯のエラー率は明確に下がります。並列度が原因で詰まっている場合は、続けて p-limit での並列制御を入れる、という順番で進めるのが安全でした。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API & SDK2026-05-29
Claude API に画像 URL を渡すと invalid_request_error が出るときの切り分け
Claude API の Messages で `source.type: url` を指定した画像が invalid_request_error で落ちるとき、原因は URL スキーム・MIME・サイズ・到達性のどれかにほぼ収束します。診断順に整理します。
API & SDK2026-04-28
Claude API のプロンプトキャッシュがヒットしない時の診断手順
Claude API のプロンプトキャッシュが効いていない時、まず確認すべきは usage フィールドです。cache_read_input_tokens がゼロのまま増えない原因を、5つの典型パターンに沿って切り分けます。
API & SDK2026-07-13
Claude API の同時実行を束ねる — シングルフライトで重複推論とキャッシュ・スタンピードを防ぐ
同一プロンプトが同時に何度も Claude へ飛ぶ重複推論を、シングルフライト(request coalescing)で束ねる設計です。プロセス内・分散環境の実装、ジッター付きリトライ、負のキャッシュまで実測付きでまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →