広告が嫌いですか? 行く 広告なし 今日

APIのレート制限——ヘッダー、指数バックオフ、および429エラーへの対応

更新日

429を取得しました。APIは、速度を落とすよう指示しています。X-RateLimit-*ヘッダーの解釈、Retry-Afterの理解、およびジャイターを含む指数関数的なバックオフの実装方法について説明し、あなたの統合がレート制限を適切に処理し、サーバーに過度に負荷をかけないようになります。

API Rate Limiting — Headers, Exponential Backoff, and Surviving the 429 1

429を取得しました。ウェブ훅ハンドラーがクラッシュした可能性があります。あるいは、バッチジョブが静かにドロップされた可能性があります。APIは「リクエストが多すぎ」というエラーメッセージを返し、おそらくスクロールしたであろう応答ヘッダーの壁を返しました。

これらのヘッダーがすべての物語です。どのようにこれらを読み取り、問題を悪化させないリトライロジックを書く方法を説明します。

実際に重要なヘッダー

大多数のレート制限付きAPIは、429のときだけでなく、すべての応答でこれらのヘッダーの変種を返します。

  • X-RateLimit-Limit — 現在のウィンドウにおける許容リクエスト数です。GitHubのREST APIでは、認証ユーザーには1時間あたり5,000リクエスト、未認証リクエストには60リクエストが割り当てられます。
  • X-RateLimit-Remaining — 現在のウィンドウにおける残りのリクエスト数です。この値が0になると、次のリクエストは429を返します。
  • X-RateLimit-Reset — ウィンドウがリセットされる時間(Unixエポックタイムスタンプ)です。これは開発者が最も無視するヘッダーであり、最も有用なヘッダーです。
  • X-RateLimit-Used (GitHub専用) — これまでに消費されたリクエスト数です。これは確認のためのチェックに役立ちます。 Limit - Remaining ただし、有用なチェックです。
  • Retry-After — 429応答のみに表示されます。秒数またはHTTP日付文字列です。APIがこれを送信する場合、それを使用してください。自分で計算するよりも正確です。

実際のGitHub応答ヘッダーは次のようになります:

X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4823
X-RateLimit-Reset: 1716998400
X-RateLimit-Used: 177
X-RateLimit-Resource: core

X-RateLimit-Resource ヘッダーはGitHub専用です:彼らはREST、検索、GraphQLのそれぞれに別々のクォータプールを維持しています。検索クォータ(1分あたり30リクエスト)を消費しても、コアクォータには影響しません——逆も同様です。

Stripeは異なります

Stripeは X-RateLimit-* 命名を使用しません。そのヘッダーは異なるプレフィックスで表されます。

Stripe-Ratelimit-Limit: 100
Stripe-Ratelimit-Remaining: 97
Stripe-Ratelimit-Reset: 1716998460

そして429のとき:

Retry-After: 30

Stripeのデフォルト制限は100リクエスト(ライブモード)です。 秒あたりこれは1時間あたりではなく、これは重要です:500人の顧客をインポートするループが、あなたが自らの端末で制限を適用していない場合、5秒以内にそのウィンドウを消費します。

Stripeはリクエストレート制限とリソースごとの制限(例:短いブーストで多くの顧客を作成)を区別しています。429応答ボディは、どの制限に達したかを指定しています——常に応答ボディの全内容をログに記録してください。

リセットタイムスタンプの解読

X-RateLimit-Reset 値はUnixエポックタイムスタンプです。 1716998400 一見すると何も示しませんが、簡単に解読できます:を用いて、UTC時刻に変換し、リセットまでの距離を確認できます。 Unixタイムスタンプコンバーター コードでは:

リセットまでの秒数を示します。しかし、まずを確認してください——クォータがまだ残っている場合は、待機する必要はありません。 reset_time - time.now() 429のボディが示す内容 X-RateLimit-Remaining 429ステータスコードだけでは不十分です。応答ボディは、どの制限に達したかを通常指定しています:

Stripe:

OpenAIはさらに進んでいます:エラーメッセージが、1分あたりのトークン制限か、1分あたりのリクエスト制限かを指定しており、これはリトライ戦略を完全に変える可能性があります。常に応答ボディの全内容をログに記録してください。

彼らのOG画像には、投稿のタイトル、日付、読書時間が含まれています。

{
  "message": "API rate limit exceeded for user ID 12345.",
  "documentation_url": "https://docs.github.com/rest/overview/rate-limits"
}

指数的バックオフとジャイター

{
  "error": {
    "code": "rate_limit",
    "message": "Too many requests hit the API too quickly.",
    "type": "invalid_request_error"
  }
}

簡単な解決策:429をキャッチし、1秒の待機時間を設け、リトライします。これは2つの理由で失敗します:

複数のワーカーが同じエンドポイントにアクセスしている場合、すべてが1秒の待機時間を設け、同時にリトライします——これは同期されたリトライ嵐を再現し、問題を再び作り出します。

1秒は、1時間または1日あたりのクォータを消費した場合に無意味です。あなたは3,600個の429を収集するだけです。

  • 正しいアプローチは、指数的バックオフとジャイターです:各リトライは前のリトライよりも長く待機し、ランダムな成分を加えることで、並列ワーカーのリトライを非同期化します。
  • この実装における優先順位は意図的です:

Retry-Afterが最初

import time
import random
import requests

def fetch_with_backoff(url, headers, max_retries=5):
    base_delay = 1  # seconds

    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code != 429:
            return response

        # Prefer Retry-After if the API provides it
        retry_after = response.headers.get("Retry-After")
        if retry_after:
            wait = int(retry_after)
        else:
            # Fall back to X-RateLimit-Reset
            reset = response.headers.get("X-RateLimit-Reset")
            if reset:
                wait = max(0, int(reset) - int(time.time()))
            else:
                # Pure exponential backoff with full jitter
                cap = 60  # max wait: 60s
                wait = random.uniform(0, min(cap, base_delay * (2 ** attempt)))

        print(f"Rate limited. Attempt {attempt + 1}/{max_retries}. Waiting {wait:.1f}s")
        time.sleep(wait)

    raise Exception(f"Max retries exceeded after {max_retries} attempts")

— APIが正確に待機時間を示している場合、それを使用してください。自分で計算するよりも二の足を踏みません。

  • X-RateLimit-Resetをフォールバックとして — 実際のリセットまでの秒数を計算し、固定の遅延を推測するのではなくします。
  • 完全なジャイターを最終手段として — バックオフウィンドウ全体にリトライを分散します。AWSのアーキテクチャブログはこれを「完全なジャイター」と呼び、等間隔ジャイターまたはジャイターなしと比較して、サーバー側の衝突を測定的に減少させていると述べています。
  • リセット時random.uniform(0, cap) — リセットタイムスタンプが計算するとき、過去に存在する可能性があります。負の待機値がハンドラーをクラッシュしないように注意してください。
  • max(0, ...) よくある誤り 429以外のエラーをレート制限エラーと誤認する。

503はサーバー側のエラーです。401は認証情報が間違っていることを意味します。まずを確認してから、レート制限リトライロジックを適用してください。

429を無視して空データを返す。 静かな失敗は、例外が発生するよりもデバッグが難しいです。エラーを表面化してください。 status_code == 429 固定遅延を使用する。

1時間のウィンドウで47分が残っている場合、5秒の待機はあなたに何も得ません。リセットタイムスタンプから計算してください。 無限にリトライする。

上限を設定し、それを超えた場合に例外を上げます。いくつかの429は、次の請求期間まで回復しないクォータ枯渇を示しており、無限リトライループはバグです。 X-RateLimit-Remainingを積極的に監視しない。

が10%未満になると、ゼロに達する前にリクエストを間隔をあけてください。ほとんどのSDKは自動的にこれを実行しません。コストは数ミリ秒の追加遅延ですが、429を最初に見ることを防ぐメリットがあります。 429は一度だけの問題ではなく、繰り返しの制限です。それに関連するヘッダーを無視すると、同じ壁に何度もぶつかり続けます。APIが提供する場合、それを使用してください。提供しない場合、から計算してください。ジャイターを加えてリトライを同期させず、上限を設定して無限リトライループがプロダクション事故になるようにしないでください。 max_retries そして、が表示され、その実際の時間に気づきたいときに、が1クリックでそれを示します。

APIレート制限 — ヘッダー、指数的バックオフ、そして429を乗り越える 2 APIレート制限 — ヘッダー、指数的バックオフ、そして429を乗り越える 1 Remaining 10%未満に下がる Limit、ゼロに達する前にリクエストを間隔をあけて送信してください。ほとんどのSDKは自動的にこれを行いません。コストは数ミリ秒の追加遅延ですが、メリットは429エラーを最初に見ることを防ぐことです。

まとめ

429エラーは一度だけの問題ではなく、繰り返し発生する制限です。そのヘッダーを無視すると、同じ壁に何度もぶつかり続けます。使用してください。 Retry-After when the API provides it. Calculate from X-RateLimit-Reset 提供されない場合に計算します。リトライを同期させないようジャイターを追加し、無限リトライループが生産環境の事故になるように上限を設定します。

そして、に注目しながら、それが実際にいつになるかを確認しているとき—— X-RateLimit-Reset: 1716998400 は、ワンクリックで教えてくれます。 Unixタイムスタンプコンバーター APIレート制限 — ヘッダー、指数関数バックオフ、そして429を乗り越える 2

広告なしで楽しみたいですか? 今すぐ広告なしで

拡張機能をインストールする

お気に入りのブラウザにIOツールを追加して、すぐにアクセスし、検索を高速化します。

に追加 Chrome拡張機能 に追加 エッジ拡張 に追加 Firefox 拡張機能 に追加 Opera 拡張機能

スコアボードが到着しました!

スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!

ニュースコーナー 技術ハイライト付き

参加する

価値ある無料ツールの提供を継続するためにご協力ください

コーヒーを買って