ブログに戻る

トラブルシューティングガイド

AI API からの 429 Too Many Requests: バックオフ付きリトライ

AI API からの 429 の意味と対処法: ジッター付き指数バックオフ、Retry-After、エージェントの並列数制限、Python と JavaScript のコード付き。

投稿者: CheapTokens Team公開日 更新日 読了時間 8 分

リクエストループの図: 429 レスポンス、遅延が増加する待機ステップ、リトライ
このページの内容
  1. 429 の意味
  2. 遅延を推測する前にヘッダーを読む
  3. Python: ジッター付きバックオフ
  4. JavaScript: 同じループ
  5. エージェントが 429 を処理する方法
  6. CheapTokens でエージェントループを実行する
  7. それでも失敗する場合

AI API からの 429 Too Many Requests は、リクエストを送りすぎるのが速すぎたため、またはアカウントの制限に達したためにリクエストが拒否されたことを意味します。対処法は、指数バックオフとジッターを使って待機してリトライすること、レスポンスに Retry-After ヘッダーがある場合はそれを使うこと、エージェントが並列で送るリクエスト数を制限することです。実際には支出や残高の制限である 429 をリトライしないでください。待っても解決しません。

このガイドは OpenAI と Anthropic の公式レート制限ガイドに基づいており、どちらも 2026年10月22日に確認しました。どちらのページにも最終更新日は表示されていません。

429 の意味#

OpenAI は 429 を、レート制限を超えたために拒否されたリクエスト(rate_limit_error、トラフィックが急増した場合はコード slow_down)、またはハードな支出制限に達したためと説明しています。Anthropic も 429 に rate_limit_error を使います。支出上限による 429 には retry-after ヘッダーがなく、アクセスが再開されるまで失敗し続けます。

紛らわしい隣接ケースが2つあります。OpenAI の 503 server_is_overloaded と Anthropic の 529 overloaded_error は過負荷状態であり、あなたのレート制限ではありません。通常は同じリトライループで処理されますが、原因については異なることを示しています。

つまり3つのケースがあり、それぞれ異なる対応が必要です:

レスポンス 原因 対処
429 レート制限 1分あたりのリクエスト数またはトークン数が多すぎる 待機し、バックオフでリトライ
429 支出または残高制限 アカウントの制限に達した リトライしない。制限を修正するか残高を補充する
503 / 529 過負荷 プロバイダー側が混雑している 上限を設けてバックオフでリトライ

残高が尽きた場合は、代わりに「トークンが足りない」エラーをお読みください。タイムアウトや接続拒否などのネットワークレベルの障害については、一般的な接続エラーを参照してください。

遅延を推測する前にヘッダーを読む#

OpenAI はレート制限されたレスポンスにこれらのヘッダーを記載しています: Retry-After、および x-ratelimit-limit-requests、x-ratelimit-remaining-requests、x-ratelimit-reset-requests と -tokens のバリアント。他の API はこれらの一部またはまったく送らない場合があるため、クライアントは Retry-After が存在する場合にそれを使い、存在しない場合は独自のスケジュールにフォールバックするように書いてください。

OpenAI のリトライに関するガイダンスを要約すると:

  • Retry-After を最小待機時間として扱い、その上に小さなランダム遅延を加える。
  • それがなければ、ジッター付きの指数バックオフを使う。
  • 試行回数と合計リトライ時間の両方に上限を設ける。
  • SDK のリトライの上に独自のリトライを重ねない。
  • クォータや請求のエラーをリトライしない。
  • 失敗したリクエストも1分あたりの制限にカウントされるため、積極的なループは状況を悪化させる。
  • ストリーミングでは、すでに出力を消費した後にリクエストを再生しない。

Python: ジッター付きバックオフ#

公式の openai パッケージは OpenAI 互換の任意のエンドポイントで動作します。SDK 自身のリトライをオフにして(max_retries=0)、あなたのリトライと掛け合わさらないようにしてください。モデルの id は CheapTokens のドキュメントから取得します。id はプロバイダー間で異なります。

python
import os
import random
import time

from openai import OpenAI, APIStatusError

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
    max_retries=0,  # we retry ourselves
)

RETRYABLE = {429, 500, 502, 503, 504}


def chat_with_retry(model, messages, max_attempts=5, max_total=60.0):
    started = time.monotonic()
    for attempt in range(max_attempts):
        try:
            return client.chat.completions.create(model=model, messages=messages)
        except APIStatusError as err:
            last = attempt == max_attempts - 1
            if err.status_code not in RETRYABLE or last:
                raise
            header = err.response.headers.get("retry-after")
            if header and header.replace(".", "", 1).isdigit():
                delay = float(header) + random.uniform(0, 1)  # minimum wait + jitter
            else:
                delay = random.uniform(0, min(30.0, 2.0 ** attempt))  # full jitter
            if time.monotonic() - started + delay > max_total:
                raise
            time.sleep(delay)

ヘッダーがない場合の遅延の上限は 1、2、4、8 秒と増加し(最大30秒)、random.uniform はその下のランダムな値を選ぶため、多くのワーカーが同時に目覚めることはありません。Retry-After は HTTP 日付の場合もあるため、上記のコードは数値形式を処理し、それ以外はバックオフにフォールバックします。

JavaScript: 同じループ#

javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.cheaptokens.dev/v1",
  apiKey: process.env.CHEAPTOKENS_API_KEY,
  maxRetries: 0, // we retry ourselves
});

const RETRYABLE = new Set([429, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

export async function chatWithRetry(model, messages, maxAttempts = 5, maxTotalMs = 60000) {
  const started = Date.now();
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await client.chat.completions.create({ model, messages });
    } catch (err) {
      const last = attempt === maxAttempts - 1;
      if (!RETRYABLE.has(err.status) || last) throw err;
      const header = Number(err.headers?.get?.("retry-after"));
      const delayMs = Number.isFinite(header) && header > 0
        ? header * 1000 + Math.random() * 1000 // minimum wait + jitter
        : Math.random() * Math.min(30000, 2 ** attempt * 1000); // full jitter
      if (Date.now() - started + delayMs > maxTotalMs) throw err;
      await sleep(delayMs);
    }
  }
}

うまくいけば、レート制限された呼び出しは単に時間がかかるだけで、その後は通常の補完を返します。最後の試行の後も失敗する場合は元のエラーが送出されるため、コードでログに記録したり、ジョブを後で処理するためにキューに入れたりできます。

エージェントが 429 を処理する方法#

エージェントループは多くの短い呼び出しを送り、複数のエージェントやツール呼び出しが同時に実行されることがあります。リトライだけでは不十分です。リクエストレートを根本から下げてください:

  1. 並列数を制限する。 3〜5 の並列呼び出しのセマフォが妥当な出発点です。エラーがまれな間だけ増やしてください。
  2. 1つのリミッターを共有する。 複数のワーカーが同じキーを使う場合、同じ制限を共有するため、ワーカーごとに1つのリトライループでは不十分です。プロセスごとに1つのキューまたは1つのセマフォを使ってください。
  3. グローバルにバックオフする。 1つの呼び出しが遅延付きの 429 を受けたら、失敗した呼び出しだけでなくプール全体をその時間停止してください。
  4. リクエストを小さく保つ。 リクエストあたりのトークン数が少ないほど、1分あたりのトークン制限への負荷が減ります。履歴とツール出力を削減してください。トークンがどこに使われるかはAI エージェントのコストを見積もる方法を参照してください。
  5. リトライ不可能なエラーで停止する。 401、400、または残高エラーは自然に解決しません。早く失敗して報告してください。
python
import asyncio

limit = asyncio.Semaphore(4)  # at most 4 requests in flight


async def run(job):
    async with limit:
        return await call_model(job)  # your async call with the retry loop above

CheapTokens でエージェントループを実行する#

多くの短い呼び出しを行うエージェントループには、CheapTokens は MiniMax-M2.7 を提供しています: 時間帯を問わず1つの価格、180K コンテキスト、回答あたり最大 8,192 トークン、すべてのモデルで1つの残高。出力は 100万トークンあたり $0.15 で、入力と出力を合計すると OpenRouter より安価です。OpenAI 互換エンドポイント https://api.cheaptokens.dev/v1 を通じて動作します。上記のコードはすでにこのエンドポイントを指しています。CheapTokens のドキュメントからモデル id を設定して実行してください。

入力 500,000、出力 100,000 のワークロードの実際の価格:

モデル コスト OpenRouter
GLM-5.3-Flash $0.014 $0.0282
DeepSeek-V4-Flash $0.0175 $0.0394
MiniMax-M2.7 $0.04 $0.27

合計 500,000 入力トークンと 100,000 出力トークン。100万トークンあたりの価格は現行のもので、当社の料金体系に合わせて変動します。OpenRouter 列は同じワークロードの参考価格を示しています。

すべての価格、セットアップドキュメントを参照し、キーを作成して最初のリクエストを送信してください。

それでも失敗する場合#

  • 最初のリクエストで 429。 レスポンスボディのエラータイプとメッセージを読んでください。支出またはアカウント制限の 429 は、制限が引き上げられるかアクセスが再開されるまでリトライで失敗し続けますが、レート制限の 429 は一時停止後に解消します。
  • 長いループで 429 が続く。 リトライを重ねている可能性があります: SDK のリトライ × あなたのリトライ。例のように SDK をゼロに設定してください。
  • バッチの開始時にエラーが集中する。 すべてのワーカーを一度に起動するのではなく、徐々に立ち上げてください。OpenAI は急増するトラフィックに対して slow_down を報告しています。
  • ストリーミング呼び出しが途中で失敗した。 出力を消費した後に盲目的に再生しないでください。回答全体を再開するか、すでに得たものから続けるかを決めてください。
  • レスポンスに Retry-After がない。 コードはジッター付きバックオフにフォールバックするため、変更は不要です。