深夜23時1分に、バッチ処理が止まった。
ログには 429 Too Many Requests とだけあった。翌朝確認すると、23時10分にはリセットされており、9分だけ待てば動いていた計算になる。でもその9分を誰も待てなかった——無人バッチだから。
それが3日続いた。原因を調べてようやく分かったのは、Claude Codeの利用上限は「5時間ごとのローリングウィンドウ」で管理されているという仕組みだった。
この記事で分かること
- Claude Codeの429エラーが「毎晩同じ時刻に」起きる理由
- 5時間ローリングウィンドウの仕組みと計算方法
- スケジュール分散で429を減らす設計パターン3つ
- コスト・ターン数の記録方法(
--output-format json)
Claude Codeの利用上限は「時刻リセット」ではなく「5時間の窓」
Claude Codeには利用上限(rate limit)がある。上限に達すると 429 Too Many Requests が返り、一定時間が経過するまで新しいリクエストができなくなる。
ここで多くの人が誤解しやすい点がある。「毎日0時(あるいは毎朝9時)にリセットされる」という理解は正しくない。 実際の仕組みは「5時間のローリングウィンドウ(転がる窓)」だ。
| 誤った理解 | 正しい仕組み |
|---|---|
| 毎日決まった時刻にリセット | 常時5時間の窓で使用量を管理 |
| 上限まで使えば翌日リセット待ち | 5時間前の使用量が随時消えていく |
| 日次の使用量をカウント | 直近5時間の使用量をカウント |
具体的には:
- 17:00に大きな処理(例:$3の消費)を実行したとする
- その$3のカウントは 22:00 に消える
- 22:59時点で上限に達していた場合、22:00以降に消えていくカウント分だけ容量が回復する
- 「今夜はもう無理だ」と思っても、5時間前の履歴が消えれば動き出す
この仕組みを知らずに「重い処理を夕方以降にまとめて実行する」設計にすると、毎晩ほぼ同じ時刻に上限に達する。
実際に起きた失敗パターン
自動化バッチを複数の工程に分けて設計していた。各工程はClaude Codeの -p(無人実行)モードを使ったセッションで、1工程あたり数十〜100ターン程度、コストは$1〜$5程度。
問題は、全工程が17:00〜23:00の6時間帯に集中していたことだ。
17:00 -- 工程A(最重要・最コスト大)
18:30 -- 工程B(中程度)
21:00 -- 工程C(再試行。A/Bが失敗した日だけ動く)
23:00 -- 工程D(軽い確認処理)
実際のログで確認できた流れ:
- 21:13に工程Cの再試行が動き始め、84ターン・$3.63相当を消費
- その使用量が5時間後(翌02:13)まで積み上がる
- 23:01に工程Dが開始するが、17:00〜23:01の累積が上限を超えており429発生
- 23:10に17:00分のカウントが一部消え、再起動できた
「毎晩23時過ぎに止まる」というのは偶然ではなく、スケジュールの構造から必然的に起きていた。
さらに厄介だったのは、「工程Cは失敗した日だけ動く」という再試行の設計だ。工程Aや工程Bが失敗すると再試行が入り、その日の消費量が膨らむ。失敗率が上がるほど上限に達やすくなる——つまり問題が起きている日ほど夜に止まりやすいという悪循環だった。
解決策:5時間の窓を意識してスケジュールを分散する
根本的な解決策は単純で、工程を1日に広げて分散することだ。
パターン1:昼の枠を使う
10:00 -- 工程A前半(重い処理の一部)
12:30 -- 工程B
15:30 -- 工程C
17:00 -- 工程A後半
18:30 -- 工程D前半
20:00 -- 工程D後半
17:00〜23:00の窓に入る処理量が半分以下になる。実際に切り替えてから2週間で429エラーは週1件以下に減った(以前は週4〜5件)。
パターン2:重い工程を早い時間帯に前倒しする
5時間ローリングの性質上、重い工程ほど朝〜昼に配置すると夕方以降の枠が空く。
朝10時に$5の工程を実行したとすると、その$5のカウントは15:00に消える。17:00以降の処理には余裕が生まれる。
逆に夕方17時に$5を使うと、22:00まで圧迫が続く。「夜に重い処理を集中させない」だけで状況は改善する。
パターン3:再試行の設計を見直す
失敗した工程を自動再試行する仕組みがある場合、再試行のタイミングと上限回数を設計に含める必要がある。
- 再試行を当日中に行わず翌朝の空き枠で実行する
- 再試行は1日1回だけに制限する
- 「今日は再試行しない」という判断をする時間帯を決めておく
再試行が最も429を引き起こしやすいのは「元の工程が失敗して、かつ当日の消費量が多い日」だ。失敗が重なる日は特に注意が必要。
コストとターン数を記録しておく
429が発生した原因を調べるには、「どの工程がいつどれだけ消費したか」の記録が必要だ。
Claude Codeは --output-format json フラグをつけると、実行後にJSON形式で使用量を出力してくれる。
claude -p "実行したいプロンプト" --output-format json > output.json
出力のJSONには以下のフィールドが含まれる:
{
"num_turns": 84,
"usage": {
"input_tokens": 123456,
"output_tokens": 8765,
"cache_read_input_tokens": 3856789
},
"modelUsage": [
{ "model": "claude-sonnet-4-6", "outputTokens": 8700, ... }
]
}
これを集計スクリプトで読み込み、工程ごとの実績を時系列で記録しておく。429が発生した日のログを見ると、どの工程・何時・何ターンで消費が膨らんだかが分かる。
注意: num_turns の値が小さくても、実際の消費が大きいケースがある(内部処理がターンカウントに乗らない場合)。modelUsage の outputTokens の合計で実消費を確認する方が正確だ。
まとめ:「429が出た時刻」より「5時間前に何をしたか」を見る
Claude Codeの利用上限は5時間ローリング方式。「毎晩同じ時刻に止まる」のは、スケジュールの構造的な問題であることが多い。直し方は、重い工程を昼間に分散させるだけ。
深夜に429が出て調べ始めると、「5時間前に何があったか」を確認するのが最初のステップだ。
スケジュールが固定の無人バッチであれば、一度「5時間ウィンドウに何が入っているか」を図示してみると、集中しているポイントが目に見える形になる。そこを分散させるだけで、繰り返しの429は大幅に減らせる。
皆さんの自動化バッチは、1日のどの時間帯に集中していますか?
関連記事: