0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude API の 429(rate_limit_error)を正しく捌く実装手順 — RPMではなくトークンバケツ・retry-after・SDK自動リトライの3つのハマりどころ【2026】

0
Posted at

はじめに / 対象と前提

Claude API を叩くバッチ処理を書いていたら、途中から 429 rate_limit_error を連発して止まった。リクエスト数は毎分 20 回程度でレート制限(RPM)には全然届いていないはずなのに、なぜか 429 が返る。調べたら 制限はリクエスト数だけでなくトークン数にも別枠で存在する ことが原因だった。この記事では自分がハマった内容と、429 を正しく捌くリトライ実装をまとめる。

想定読者

  • Claude API(Anthropic API)を Python から直接叩いている人
  • 単発では動くが、まとめて回すと 429 で落ちる人

環境

  • Python 3.13
  • anthropic SDK 0.6x 系
  • Claude API(Messages API)/ 2026 年 9 月時点で動作確認

TL;DR

  • レート制限は RPM(リクエスト/分)・ITPM(入力トークン/分)・OTPM(出力トークン/分)の 3 本立て。RPM に余裕があってもトークン側で 429 になる
  • 429 レスポンスの retry-after ヘッダー(秒)に従って待つ のが正解。自前の指数バックオフだけで済ませない
  • anthropic SDK は デフォルトで 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 を明示する
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?