429 レート制限の実装は一通り済んだのに、ある日突然「529 Overloaded」がログに並び始めて焦った、という経験はないでしょうか。私も本番投入直後のある夕方、Slack 連携ボットのエラー通知が止まらなくなり、慌ててステータスページを開いたことがあります。結論を先に書くと、529 は自分のアプリのせいで起きるエラーではないため、従来の「リクエストを減らす」発想では解決しません。Anthropic 側のキャパシティが一時的に逼迫しているシグナルなので、待ち方・流し方・逃がし方の三点で設計する必要があります。
ここでは529 エラーの正体を押さえたうえで、私が本番環境で実際に採用している対処パターンを、コードと運用の両面から整理していきます。
529 Overloaded は「自分のせいではないエラー」
Anthropic の公式エラーコード表では、529 は「The API is temporarily overloaded」と定義されています。混同しやすいのが 429(レート制限)と 503(サービス利用不可)との違いですが、実務で意識すべきポイントはシンプルです。429 は自分のトークン使用量や RPM がプランの上限に達した時に返ります。一方 529 は、自分のクォータには余裕があってもプラットフォーム全体の需要が供給を上回った時に返ります。つまり、リクエスト間隔を広げても根本解決にならない種類のエラーです。
私が最初に 529 を踏んだ時も、リクエスト頻度は平常時の半分以下でした。ログには "type": "overloaded_error" と書かれており、Status Code が 529。Anthropic Status(status.anthropic.com)を確認すると、北米リージョンで一時的な負荷上昇が報告されていました。この時の対応として有効だったのは、レート制限の緩和ではなく、後述のリトライ戦略とモデル切替の組み合わせです。
まずログで「本当に 529 か」を見分ける
対処を始める前に、エラーが本当に 529 なのかを確認しておきましょう。SDK によってエラークラス名が異なり、汎用的な APIError で握りつぶしていると判別できません。Python SDK の場合、以下のように anthropic.APIStatusError のサブクラスで分岐するのが確実です。
import anthropic
from anthropic import APIStatusError, APIConnectionError
client = anthropic.Anthropic()
def call_with_classification(messages):
try:
return client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=messages,
)
except APIStatusError as e:
# e.status_code で 429 / 529 / 500 系を識別する
err_type = getattr(e.response.json().get("error", {}), "get", lambda k: None)("type")
print(f"[API {e.status_code}] type={err_type}")
raise
except APIConnectionError as e:
print(f"[Connection] {e}")
raise
# 実行すると、529 のときは [API 529] type=overloaded_error が出力されるerr_type が overloaded_error と rate_limit_error のどちらかで、後段のリトライ戦略を分岐させます。ここを分離しておかないと、「ひたすら長く待つ」だけの乱暴なバックオフになりがちです。
指数バックオフ + Jitter は必須だが、それだけでは足りない
529 向けの待ち方の基本は、指数バックオフにランダムな揺らぎ(Jitter)を加える方式です。全クライアントが同じ秒数で一斉リトライすると、復帰直後にまた 529 を誘発するからです。私が使っているのは「Full Jitter」と呼ばれるパターンで、公式の AWS アーキテクチャブログ で詳しく論じられています。
import random
import time
from anthropic import APIStatusError
def call_with_full_jitter(fn, max_retries=6, base=1.0, cap=60.0):
"""
Full Jitter によるリトライ。cap で待機上限を設ける。
429/529 でのみリトライし、400 系(Bad Request)は即座に再送しない。
"""
attempt = 0
while True:
try:
return fn()
except APIStatusError as e:
if e.status_code not in (429, 529, 503):
raise
if attempt >= max_retries:
raise
# Full Jitter: 0 から min(cap, base * 2^attempt) の一様乱数
sleep = random.uniform(0, min(cap, base * (2 ** attempt)))
time.sleep(sleep)
attempt += 1
# 期待する挙動: 529 連発時に、1 回目は最大 1 秒、2 回目は最大 2 秒、
# 3 回目は最大 4 秒... と指数的に広がりつつ、同期リトライを防ぐただし、529 の発生源が Anthropic 側の広域の混雑である場合、指数バックオフだけでは復帰に時間がかかります。ユーザーに待ってもらえないリアルタイム用途では、次に挙げるフォールバックの併用が現実解です。
本番で効く「モデル切替」と「地域分散」のフォールバック
混雑時にも応答を返したい場合、最上位モデルに固執せず、動的にモデルを切り替える実装が効きます。具体的には、Sonnet で 529 が連発したら Haiku に切り替えて再試行するパターンです。品質は落ちますが、503 的な完全断絶よりマシな応答を返せます。
MODEL_CASCADE = ["claude-sonnet-4-6", "claude-haiku-4-5"]
def resilient_call(messages, max_retries_per_model=3):
last_err = None
for model in MODEL_CASCADE:
try:
return call_with_full_jitter(
lambda: client.messages.create(
model=model,
max_tokens=1024,
messages=messages,
),
max_retries=max_retries_per_model,
)
except APIStatusError as e:
if e.status_code != 529:
raise
last_err = e
# 次のモデルにフォールバック
continue
raise last_errもうひとつ有効なのが、Anthropic 直 API と Amazon Bedrock / Google Cloud Vertex AI を併用する地域分散です。リージョンやゲートウェイごとに混雑の度合いが異なるため、片方が 529 を返していてももう片方は正常応答、というケースが実際にあります。セットアップは少し手間ですが、SLA を守る本番サービスなら投資に見合います。具体的な Bedrock 構成は同サイトの Claude API × Amazon Bedrock 本番構築ガイド で扱っています。
サーキットブレーカーで「無駄な再送」を止める
リトライと並んで重要なのが、「壊れているときに送り続けない」仕組み、いわゆるサーキットブレーカーです。529 が短時間に一定回数以上発生したら、一時的に Anthropic 直 API への送信を停止し、フォールバック経路だけに流す運用にします。
私は pybreaker(Python)や opossum(Node.js)を使っていますが、自前で 30 行ほど書いても十分実用になります。下記は 60 秒間に 5 回 529 が出たら 2 分間開放(Open)するシンプルな実装例です。
import time
from collections import deque
from threading import Lock
class CircuitBreaker:
def __init__(self, failure_threshold=5, window_sec=60, open_sec=120):
self.failures = deque()
self.window = window_sec
self.threshold = failure_threshold
self.open_sec = open_sec
self.opened_at = None
self.lock = Lock()
def allow(self):
with self.lock:
now = time.time()
if self.opened_at and now - self.opened_at < self.open_sec:
return False # Open 状態: 通さない
if self.opened_at:
self.opened_at = None # Half-Open → クローズへ
return True
def record_failure(self):
with self.lock:
now = time.time()
self.failures.append(now)
while self.failures and now - self.failures[0] > self.window:
self.failures.popleft()
if len(self.failures) >= self.threshold:
self.opened_at = now
# 期待する挙動: 60 秒間に 5 回 529 が出たら、以降 120 秒は allow() が False を返し
# 上位のフォールバック処理に切り替わるサーキットブレーカーを入れておくと、Anthropic 側の障害が長引いた際に「リトライで無駄にトークンを消費する」事態を防げます。監視ダッシュボードで Open 回数を可視化しておくと、事後の振り返りもしやすくなります。
サーキットブレーカー単体というより、「どこで計測し、どこで諦めるか」の思考の型が手に入る一冊です。
Priority Tier と Batches API の活用余地
最後に、コード以外のレイヤーで打てる手も押さえておきます。
まず、Anthropic の Priority Tier(従量課金プランの上位ティア)では、通常の Standard ティアより 529 発生時の優先度が高くなります。B2B で SLA が厳しい用途なら検討に値します。関連する実装パターンは Claude API 本番リミット・請求ガイド に詳しくまとめています。
もうひとつが Batches API の活用です。リアルタイム性を要求しない処理(夜間のサマリ生成、バッチ分析など)を Batches API に逃がすと、同期リクエストの総量が減り、結果として 529 に遭遇する確率も下がります。非同期で 24 時間以内に返ってくれば十分なワークロードなら、同サイトの Claude API Batch 非同期処理ガイド を参考に移行してみてください。
明日から手を打てる一歩
529 の対処は、完全に防ぐことではなく「発生しても品質を崩さず応答を返す」ことを目標にすると考えやすくなります。まず今日のうちに、本番コードに Full Jitter のリトライが入っているか確認してみてください。ここが未実装の場合、大抵のサービスでは上記の 30 行ほどの実装を追加するだけで、体感する 529 エラーの半分以上は静かに消えます。そのうえでサーキットブレーカーとモデルカスケードを段階的に足していくと、Anthropic 側の混雑に耐えるサービスに仕上がっていきます。