GitHub API のレート制限を超えました: その意味と対処方法

GitHubは、アプリが送信できる REST API 要求の数を制限します。 制限を超えると、GitHub は、制限がリセットされるか待機時間が経過するまで、403 または 429 の状態で要求を拒否します。 GitHubには 2 種類の制限があります。1 時間あたりの要求に対する主な制限と、急増を防ぐセカンダリ制限です。 レート制限されている間もアプリがリクエストを送信し続けると、GitHub があなたのインテグレーションを禁止する可能性があります。 詳細については、「REST API のレート制限」を参照してください。

GitHub のレート制限とは

Limit Value それを超えるタイミング
主に、非認証 1 時間あたり、IP アドレスあたり 60 件の要求 403 または 429、 x-ratelimit-remaining が 0
主に、個人用アクセス トークン 1 時間あたり 5,000 件の要求 同上
主に、GitHub Actions の GITHUB_TOKEN 1 個のリポジトリあたり 1 時間ごとに 1,000 件の要求 同上
Secondary たとえば、同時要求数は 100 個以下、REST エンドポイントの場合は 1 分あたり 900 ポイント、コンテンツ生成要求数は 1 分あたり約 80 個です。 403 または 429 とエラー メッセージ。 retry-after が存在する可能性があります。

GitHubは、予告なしにセカンダリの制限を変更できます。また、その制限にどの程度近づいているかを確認する方法はありません。

すべての応答には、プライマリ制限の現在の状況を示すヘッダーが含まれています。

Header それがあなたに伝えるもの
x-ratelimit-limit 1 時間あたりに送信できる要求の最大数
x-ratelimit-remaining 現在のウィンドウで残っているリクエスト数
x-ratelimit-used 現在のウィンドウで送信した要求の数
x-ratelimit-reset ウィンドウがリセットされるとき(UTC エポック秒)
x-ratelimit-resource 要求がどの制限に対してカウントされるか

GitHub のレート制限に対処する方法

  1. レート制限とアクセス許可エラーを見分けます。 GitHub は、トークンにアクセスがない場合にも 403 を返します。 応答に retry-afterがなく、 x-ratelimit-remaining が 0されておらず、メッセージにレート制限が記載されていない場合は、アクセス許可の問題です。 再試行しないでください。
  2. 最初に retry-after に従います。 ヘッダーが存在する場合は、その秒数待ちます。
  3. それ以外の場合は、リセットを待ちます。 x-ratelimit-remainingが0の場合は、x-ratelimit-resetの時刻まで再試行しないでください。
  4. そうでない場合は、1 分以上待ってください。 どちらのヘッダーもないセカンダリ制限の場合、GitHub は少なくとも 1 分間待機し、再試行が失敗するたびにさらに長く待つよう求めます。 設定した回数の再試行後に停止し、エラーを発生させます。
  5. 使い切ってしまう前にペースを落とします。x-ratelimit-remaining と x-ratelimit-reset を使用して、要求のペースを設定します。 GitHubによって制限が変更される可能性があるため、正確な残りの数に関するロジックを構築しないでください。 x-ratelimit-* ヘッダーは、GET /rate_limit エンドポイントではなく、信頼できる情報源です。
async function githubWaitMs(response, attempt) {
  if (response.status !== 403 && response.status !== 429) {
    return null;
  }
  const retryAfter = response.headers.get('retry-after');
  if (retryAfter) {
    return Number(retryAfter) * 1000;
  }
  if (response.headers.get('x-ratelimit-remaining') === '0') {
    const resetMs = Number(response.headers.get('x-ratelimit-reset')) * 1000;
    return Math.max(resetMs - Date.now(), 0);
  }
  const { message = '' } = await response.clone().json().catch(() => ({}));
  if (response.status === 429 || /rate limit/i.test(message)) {
    return 60_000 * 2 ** attempt;
  }
  // A 403 without rate limit signals is a permission problem: don't retry
  return null;
}

呼び出し元は、関数が数値を返したときに再試行し、数回試行した後に停止します。

アプリがGitHubレート制限を処理することをテストする方法

開発中にGitHubレート制限に達することはめったにありません。 いくつかの要求を送信すると、1 時間あたり 5,000 件が無限にあるように感じられます。 つまり、レート制限の対応をどのようにテストするかによって、ユーザーがバグを発見する前に自分で発見できるかどうかが決まります。

Approach 見つけたもの 欠落しているもの
運用環境の待機 実際のエラー すべて、ユーザーがクリックするまで
テストで API をモックするか、コーディング エージェントにモックを記述させる 再試行ブランチを実行するかどうか GitHubの実際のヘッダーとエラー本文、および SDK の再試行ポリシー。 アプリでは、モックに到達するためにテスト専用のスイッチも必要です。
制限されるまで実際の API を呼び出す 実際の動作 最大 5,000 件の要求を処理し、セカンダリ制限をオンデマンドでトリガーすることはできず、統合が利用停止となるリスクがあります
アプリの実際のトラフィックをインターセプトし、要求に応じてレート制限の応答を返します 実際の URL、実際の SDK と再試行ポリシー、GitHub独自のヘッダーとエラー形式 アプリ自体には何の変化もないため、コードを単独でテストすることにはなりません。 それ用に単体テストを取っておいてください。

アプリで試す

Dev Proxyは、アプリのapi.github.comへの要求をインターセプトし、GitHubスタイルのレート制限応答を返しますが、アプリは実際のURLを呼び出し続けます。 github-rate-limiting プリセットでは、要求は 1 時間あたり 60 回の上限に対してカウントされ、x-ratelimit-* ヘッダーを送信し、上限に達すると API rate limit exceeded を含む 429 を返します。それまでは、要求は GitHub に送られ、実際の上限に対してもカウントされます。

プリセットをダウンロードし、それを使用して開発プロキシを起動します。

devproxy config get github-rate-limiting
devproxy --config-file "~dataFolder/configs/github-rate-limiting/.devproxy/devproxyrc.json"

セカンダリ制限をテストするには、代わりに同じフォルダーから devproxyrc-secondary.json を使用して Dev Proxy を起動します。 セカンダリ レート制限 429 を retry-after ヘッダーを伴ってランダムに返します。

その後、通常どおりにアプリを実行し、アプリがどのように動作するかを確認します。 開発プロキシをインストールするには、「 開発プロキシの設定」を参照してください。

次のステップ

参照