はじめに / 対象と前提
Claude API を叩くバッチ処理を書いていたら、途中から 429 rate_limit_error を連発して止まった。リクエスト数は毎分 20 回程度でレート制限(RPM)には全然届いていないはずなのに、なぜか 429 が返る。調べたら 制限はリクエスト数だけでなくトークン数にも別枠で存在する ことが原因だった。この記事では自分がハマった内容と、429 を正しく捌くリトライ実装をまとめる。
想定読者
- Claude API(Anthropic API)を Python から直接叩いている人
- 単発では動くが、まとめて回すと 429 で落ちる人
環境
- Python 3.13
-
anthropicSDK 0.6x 系 - Claude API(Messages API)/ 2026 年 9 月時点で動作確認
TL;DR
- レート制限は RPM(リクエスト/分)・ITPM(入力トークン/分)・OTPM(出力トークン/分)の 3 本立て。RPM に余裕があってもトークン側で 429 になる
- 429 レスポンスの
retry-afterヘッダー(秒)に従って待つ のが正解。自前の指数バックオフだけで済ませない -
anthropicSDK は デフォルトで 2 回自動リトライする。その上に自前リトライを被せると、失敗 1 回のつもりが裏で 3 倍叩いている
手順 / 動かし方
1. まず自分の制限値をヘッダーで確認する
制限値はアカウントの利用ティアとモデルごとに違う。正常応答にも毎回レート制限系ヘッダーが付いてくるので、まずこれを見る。
import anthropic
client = anthropic.Anthropic() # ANTHROPIC_API_KEY を環境変数から読む
response = client.messages.with_raw_response.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "ping"}],
)
for key in [
"anthropic-ratelimit-requests-remaining",
"anthropic-ratelimit-input-tokens-remaining",
"anthropic-ratelimit-output-tokens-remaining",
"anthropic-ratelimit-input-tokens-reset",
]:
print(key, "=", response.headers.get(key))
with_raw_response を挟むと HTTP ヘッダーが読める。*-remaining が現在の残量、*-reset が回復時刻(RFC 3339)だ。
実行結果(例):
anthropic-ratelimit-requests-remaining = 48
anthropic-ratelimit-input-tokens-remaining = 37500
anthropic-ratelimit-output-tokens-remaining = 7600
anthropic-ratelimit-input-tokens-reset = 2026-09-13T12:34:56Z
2. retry-after に従うリトライを書く
429 が返ったときは、レスポンスの retry-after ヘッダーに「何秒待てばいいか」が入っている。これを最優先で使い、無い場合だけ指数バックオフに落とす。
import time
import anthropic
client = anthropic.Anthropic(max_retries=0) # SDK側の自動リトライは切る(後述)
def call_with_retry(messages, max_attempts=5):
for attempt in range(max_attempts):
try:
return client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=messages,
)
except anthropic.RateLimitError as e:
retry_after = e.response.headers.get("retry-after")
wait = float(retry_after) if retry_after else 2 ** attempt
print(f"429: {wait}秒待って再試行 ({attempt + 1}/{max_attempts})")
time.sleep(wait)
except anthropic.InternalServerError as e:
# 529 overloaded_error はこちら側の上限ではなく Anthropic 側の混雑
if e.status_code == 529:
time.sleep(min(2 ** attempt * 5, 60))
else:
raise
raise RuntimeError("リトライ上限に到達")
動作確認として、小さい制限値のうちに連続で叩くと 429: 23.0秒待って再試行 (1/5) のように出て、待った後は普通に成功する。retry-after は状況次第で数秒〜数十秒まで変わるので、固定 sleep より確実に短く済む。
ハマりどころ
1. RPM ではなくトークンバケツ(ITPM/OTPM)で先に詰まる
自分のケースがまさにこれ。毎分 20 リクエストでも、1 リクエストに 5,000 トークン級のプロンプトを載せていたので、ITPM 側の枠を先に使い切っていた。429 のエラーメッセージにはどの制限に当たったかが書かれているので、まず本文を読む。This request would exceed your organization's rate limit of input tokens per minute のように原因が明記されている。
2. OTPM は max_tokens ベースで先取りされる
出力トークンの枠(OTPM)は、実際に生成された量ではなく リクエスト時の max_tokens を目安に先に確保される 挙動をする。「どうせ途中で止まるから」と全リクエストに max_tokens=8192 を雑に指定していたら、実出力は数百トークンなのに 429 が出た。必要な分だけ指定するように直したら解消。max_tokens は「保険で大きめ」をやると自分の首を絞める。
3. SDK の自動リトライと二重になる
anthropic SDK はデフォルトで 429 や 5xx を 2 回まで自動リトライする(max_retries=2)。これを知らずに上のような自前リトライを書くと、1 回の「失敗」の裏で実際には 3 リクエスト飛んでいて、レート制限の回復をさらに遅らせる悪循環になる。方針はどちらかに寄せる:
- 自前でログや待ち時間を制御したい →
Anthropic(max_retries=0)にして自前リトライ - 楽をしたい → SDK に任せて
max_retriesを増やすだけにする(retry-afterも SDK が勝手に尊重する)
背景・補足
制限が 3 本立てなのは、少数の巨大リクエストでも大量の小リクエストでも、どちらでも計算資源を守れるようにするためだと思われる。なおプロンプトキャッシュのヒット分(cache read)は、モデルによっては ITPM にカウントされない扱いになっている。キャッシュを効かせた定型プロンプトの多い処理では、体感のレート制限がかなり緩くなるので、ヘッダーの input-tokens-remaining の減り方を一度観察してみると面白い。
利用ティア(Tier)は入金額に応じて自動で上がり、制限値も引き上がる。恒常的に 429 が出るなら、リトライで粘るよりティアを上げる(または Batches API に逃がす)方が根本解決になる。
まとめ
- Claude API のレート制限は RPM / ITPM / OTPM の 3 本立て。429 の原因はエラーメッセージ本文とレスポンスヘッダーで特定する
- リトライは
retry-afterヘッダー最優先、無ければ指数バックオフ - OTPM は
max_tokensで先取りされるので、必要以上に大きく指定しない - SDK の
max_retries(デフォルト 2)を忘れると二重リトライになる。自前実装するならmax_retries=0を明示する