API を呼び出す際、401 Unauthorized、403 Forbidden、429 Too Many Requests は最も一般的な 3 種類のエラーです。

これらはいずれも 4xx クライアントエラーに属しますが、解決方法はまったく異なります。

  • 401:認証失敗——サーバーが「あなたが誰であるか」を確認できない
  • 403:アクセス拒否——サーバーはリクエストを理解しているが、実行を許可しない
  • 429:リクエスト過多——呼び出し速度または使用量が制限を超えている

エラーの種類を区別せずに直接リトライすると、問題を解決できないだけでなく、リトライストームを引き起こし、さらには API が追加で制限される可能性があります。

本記事では、トラブルシューティングチェックリスト、curl コマンド、Node.js コードを通じて、これら 3 種類のエラーに遭遇した際の対処方法を説明します。

1 枚の表でわかる 401、403、429

ステータスコード 核心的な意味 一般的な原因 すぐにリトライすべきか
401 Unauthorized 有効な認証情報がない API Key の誤り、Token の期限切れ、認証ヘッダー形式の誤り そのままリトライすべきではない
403 Forbidden サーバーがリクエスト処理を拒否する 権限不足、IP または地域制限、リソースポリシー、WAF ブロック 通常はすべきではない
429 Too Many Requests リクエストがレートまたはクォータ制限を超えている 同時実行数が高すぎる、QPS 超過、クォータ枯渇 待機してからリトライ

注意すべき点として、401 の英語名は Unauthorized ですが、実際のトラブルシューティングでは「認証されていない」と理解するほうが適切です。403 のほうが通常、認可またはアクセスポリシーに関係します。

一、401 Unauthorized:まず認証情報を確認する

HTTP 401:API 認証情報が欠落している、無効である、または期限切れである

HTTP 401 Unauthorized は、リクエストに対象リソースに必要な有効な認証情報がないことを示します。標準的な 401 レスポンスでは通常、WWW-Authenticate レスポンスヘッダーを通じて、サーバーが期待する認証スキームをクライアントに伝えます。

一般的な原因

  1. Authorization リクエストヘッダーを送信していない
  2. API Key または Access Token の入力が間違っている
  3. Token が期限切れまたは取り消されている
  4. 認証スキームが間違っている。たとえば Bearer が欠けている
  5. 環境変数が正しく読み込まれていない
  6. 誤った環境のキーを使用している
  7. リクエストがプロキシまたはゲートウェイ経由で転送される際に、認証ヘッダーが失われた
  8. クライアントとサーバーの時刻差が大きすぎて、署名が無効になった

よくある間違い

API が Bearer Token を要求している場合:

Authorization: Bearer YOUR_ACCESS_TOKEN

しかし、クライアントが次のように送信している場合:

Authorization: YOUR_ACCESS_TOKEN

または、誤って次のように記述している場合:

Authentication: Bearer YOUR_ACCESS_TOKEN

これら 2 つのケースはいずれも 401 を返す可能性があります。

curl を使用した調査

最初からレスポンスボディだけを確認しないでください。-i を追加して、ステータスコードとレスポンスヘッダーを同時に確認します。

curl -i "https://api.example.com/v1/profile" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Accept: application/json"

重点的に確認する項目:

  • Authorization が実際に送信されているか
  • 認証スキームが正しいか
  • Token の前後にスペースまたは改行がないか
  • WWW-Authenticate が何を返しているか
  • レスポンスボディに invalid_token、expired_token などのエラー情報があるか

より詳細なリクエストプロセスを確認する必要がある場合は、次を使用できます。

curl -v "https://api.example.com/v1/profile" \
  -H "Authorization: Bearer $API_TOKEN"

注意:デバッグログには Token が含まれる可能性があります。完全なログをそのまま公開 Issue、グループチャット、またはサポートチケットに送らないでください。

401 はリトライすべきか?

同じ無効な認証情報で繰り返しリトライしないでください。

推奨フロー:

  1. リクエストに認証情報が含まれているか確認する
  2. Token が期限切れ、取り消し済み、またはコピー間違いでないか確認する
  3. Refresh Token をサポートしている場合、アクセス Token を 1 回更新する
  4. 新しい Token で 1 回リトライする
  5. それでも 401 が返る場合は、リトライを停止してエラーを記録する

固定の API Key を使用している場合は、ループしてリトライするのではなく、キーの状態を確認するか、有効なキーに変更すべきです。

二、403 Forbidden:認証は成功している可能性があるが、リクエストは依然として拒否される

HTTP 403:権限またはアクセスポリシーによりリクエストが拒否される

403 Forbidden は、サーバーがすでにリクエストを理解しているものの、処理を拒否していることを示します。典型的な権限エラーの場合、再認証しても自動的には解決しません。リクエストまたは権限設定を変更しなければ、繰り返しリクエストしても通常は失敗します。

一般的な原因

  • 現在のアカウントまたはキーに必要な権限がない
  • Token の Scope が不足している
  • 現在のプロジェクトまたはテナントに属さないリソースにアクセスしている
  • 送信元 IP がホワイトリストに含まれていない
  • リクエスト送信元の地域が制限されている
  • WAF、ファイアウォール、または不正利用防止システムによってブロックされている
  • アカウント、プロジェクト、またはリソースが無効化されている
  • API が特定のプランまたはロールにのみ公開されている
  • 署名は有効だが、署名内のリソース範囲が一致していない
  • ブラウザー環境で CSRF、Origin、または Referer ポリシーの制限を受けている

401 と 403 の違い

次の 2 文で素早く判断できます。

  • 401:サーバーがあなたが誰であるか確認できない
  • 403:サーバーはあなたの身元とリクエストを認識または理解しているが、それを許可しない

たとえば、一般メンバーが有効な Token を使用して「組織を削除する」API を呼び出す場合:

curl -i -X DELETE \
  "https://api.example.com/v1/organizations/org_123" \
  -H "Authorization: Bearer $API_TOKEN"

Token は有効でも、アカウントが組織管理者ではない場合、サーバーは次を返す可能性があります。

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": "insufficient_permissions",
  "message": "Administrator role is required."
}

403 のトラブルシューティング順序

1. レスポンスボディを読む

エラーコード、不足しているロールまたは権限、必要な Scope、拒否されたリソース ID、および IP、地域、セキュリティポリシーに関するヒントを優先して探します。

2. Token Scope を確認する

たとえば、データの読み取りには data:read だけで十分かもしれませんが、データの作成には data:write が必要になる可能性があります。Token が有効であることは、すべての権限を持っていることを意味しません。

3. リソースの所属を確認する

プロジェクト ID、組織 ID、テナント ID、ユーザー ID、リージョン、または環境が、現在の認証情報と同じアカウントまたは認可範囲に属していることを確認します。

4. ネットワークおよびセキュリティポリシーを確認する

ローカルでコードが正常に動作するのに、デプロイ後に 403 が返る場合は、サーバーの送信元 IP、IP ホワイトリスト、プロキシ設定、CDN、WAF、リクエスト元の地域、および関連するリクエストヘッダーを重点的に確認する必要があります。

ステータスコードだけで結論を出さない

実際の API 実装は必ずしも完全に一致するとは限りません。一部のプラットフォームでは、レート制限が発動した際に 429 ではなく 403 を返すことがあります。たとえば、GitHub REST API はレート制限を超えた場合に 403 または 429 を返す可能性があり、x-ratelimit-remaining、x-ratelimit-reset、retry-after などのレスポンスヘッダーを通じて制限の状態を示します。

したがって、403 が発生した場合は、次の項目も総合的に確認する必要があります。

  • レスポンスボディ内の業務エラーコード
  • Retry-After
  • X-RateLimit-Remaining
  • X-RateLimit-Reset
  • プラットフォームの公式ドキュメント

403 はリトライすべきですか?

権限不足、IP 制限、またはリソースポリシーが原因の 403 に対して、そのままリトライしても意味がありません。

正しい方法は、権限または Token Scope を変更し、正しいアカウントまたはプロジェクトを使用し、ホワイトリストとセキュリティポリシーを調整し、リソース ID を修正し、サービスまたはプランがその API をサポートしているかを確認することです。プラットフォームが、この 403 が実際には一時的なレート制限を表すと明確に説明している場合にのみ、レート制限のルールに従って待機してからリトライすべきです。

三、429 Too Many Requests:まず減速してからリトライする

HTTP 429:API リクエストがレート制限を受けているため、待機してバックオフリトライする必要があります

429 Too Many Requests は、クライアントが一定時間内に過剰なリクエストを送信した、つまりレート制限が発動したことを示します。サーバーは Retry-After を通じて、クライアントにどのくらい待機してからリトライすべきかを伝えられます。

よくある原因

  • 短時間内のリクエスト回数が多すぎる
  • 同時実行タスク数が多すぎる
  • 複数のサービスが同じ API Key を共有している
  • リトライコードに待機メカニズムがない
  • 定期タスクが同じ時刻に集中して実行される
  • リクエストがキャッシュされず、同じデータを繰り返し取得している
  • 単一アカウント、IP、またはプロジェクトのクォータが使い切られている
  • プランの使用量または残高制限

まずレスポンスヘッダーを確認する

curl -i "https://api.example.com/v1/resources" \
  -H "Authorization: Bearer $API_TOKEN"

重点的に確認する項目:

Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710000000

プラットフォームによってレスポンスヘッダーの名称は異なる場合があるため、プラットフォームのドキュメントを基準にしてください。

Retry-After を正しく処理する

Retry-After には一般的に、秒数または HTTP 日付の 2 つの形式があります。

Retry-After: 30
Retry-After: Wed, 21 Oct 2026 07:28:00 GMT

クライアントは、これら両方の形式に対応するのが望ましいです。

なぜすぐにリトライできないのですか?

リクエストがレート制限を受けた直後にリトライしても、新しいリクエストも引き続きレート制限を受けます。また、複数のクライアントが同時にリトライし、「スパイク」が発生する可能性もあります。より安全な方法は次のとおりです。

  1. Retry-After を優先して従う
  2. 明確な待機時間がない場合は、指数バックオフを使用する
  3. 待機時間にランダムなジッターを加える
  4. 最大リトライ回数を設定する
  5. 上限到達後は明確なエラーを返す

Node.js:429 を安全に処理する

以下の例では Node.js 組み込みの fetch を使用し、優先的に Retry-After を解析します。サーバーがこのレスポンスヘッダーを提供しない場合は、ランダムジッター付きの指数バックオフを使用します。

const sleep = (ms) =>
  new Promise((resolve) => setTimeout(resolve, ms));

function parseRetryAfter(value) {
  if (!value) return null;

  const seconds = Number(value);
  if (Number.isFinite(seconds) && seconds >= 0) {
    return seconds * 1000;
  }

  const timestamp = Date.parse(value);
  if (!Number.isNaN(timestamp)) {
    return Math.max(0, timestamp - Date.now());
  }

  return null;
}

async function fetchWithRetry(url, options = {}, config = {}) {
  const {
    maxRetries = 3,
    baseDelayMs = 1000,
    maxDelayMs = 30_000,
  } = config;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    if (attempt === maxRetries) {
      throw new Error(
        `请求持续受到限流,已达到最大重试次数:${maxRetries}`
      );
    }

    const retryAfterMs = parseRetryAfter(
      response.headers.get("retry-after")
    );

    const backoffCap = Math.min(
      maxDelayMs,
      baseDelayMs * 2 ** attempt
    );

    const jitterMs = Math.floor(Math.random() * backoffCap);
    const delayMs = retryAfterMs ?? jitterMs;

    console.warn(
      `收到 429,第 ${attempt + 1} 次等待 ${delayMs}ms 后重试`
    );

    await sleep(delayMs);
  }

  throw new Error("请求失败");
}

呼び出し例:

const response = await fetchWithRetry(
  "https://api.example.com/v1/resources",
  {
    headers: {
      Authorization: `Bearer ${process.env.API_TOKEN}`,
      Accept: "application/json",
    },
  },
  {
    maxRetries: 3,
    baseDelayMs: 1000,
    maxDelayMs: 30_000,
  }
);

if (!response.ok) {
  const errorBody = await response.text();
  throw new Error(`API 请求失败:${response.status} ${errorBody}`);
}

const data = await response.json();
console.log(data);

POST リクエストをリトライする場合は、冪等性も考慮する

すべてのリクエストが自動リトライに適しているわけではありません。GET、HEAD などの読み取りリクエストは、通常より安全にリトライしやすいです。注文作成、課金、メッセージ送信などの POST リクエストでは、クライアントがすでに正常に送信していても、ネットワーク中断によりレスポンスを受信できていない可能性があります。この場合に直接リトライすると、重複操作が発生する可能性があります。

プラットフォームがサポートしている場合は、書き込み操作に冪等キーを設定してください。

curl -i -X POST "https://api.example.com/v1/orders" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-20260331-0001" \
  -d '{
    "product_id": "prod_123",
    "quantity": 1
  }'

冪等キーのリクエストヘッダー名と有効期限は、具体的なプラットフォームによって決まるため、該当する API ドキュメントを参照してください。

本番環境のトラブルシューティングチェックリスト

401 を受信した場合

  • リクエストに認証ヘッダーが含まれているか
  • Bearer などの認証方式が正しいか
  • Token が期限切れ、取り消し済み、またはコピー誤りではないか
  • 正しい環境のキーを使用しているか
  • プロキシまたはゲートウェイが認証ヘッダーを削除していないか
  • クライアント時刻が正確か
  • 一度だけ安全に Token を更新できるか

403 を受信した場合

  • Token に必要な Scope が付与されているか
  • 現在のアカウントに必要なロールがあるか
  • リソースが現在のプロジェクトまたはテナントに属しているか
  • 送信元 IP がホワイトリストに含まれているか
  • 地域、WAF、またはセキュリティポリシーによる制限を受けているか
  • レスポンスボディに権限またはレート制限が明示されているか
  • レスポンスヘッダーにレート制限情報が存在するか

429 を受信した場合

  • Retry-After を読み取ったか
  • レート制限に関連するレスポンスヘッダーを確認したか
  • 指数バックオフとランダムジッターを使用しているか
  • 最大リトライ回数を設定しているか
  • 並行実行数を制限しているか
  • 重複クエリの結果をキャッシュしているか
  • リクエストを統合またはバッチ送信できるか
  • プランのクォータまたはアカウント使用量の上限に達しているか

どのような診断情報を記録することを推奨しますか?

ログには、リクエスト時刻、API ホスト名とパス、HTTP メソッド、ステータスコード、業務エラーコード、request-id または trace-id、Retry-After、レート制限の残り枠とリセット時刻、リトライ回数と実際の待機時間を記録することを推奨します。

ただし、完全な API Key、完全な Access Token、Refresh Token、ユーザーパスワード、またはリクエストボディ内の機密性の高い個人情報は記録しないでください。

最後の判断の覚え方

401:まず認証情報を修正し、古い Token でリトライしない。
403:まず権限またはポリシーを修正し、そのままリクエストを繰り返さない。
429:待機時間を尊重し、バックオフ、減速、並行数制限を行う。

ステータスコードはトラブルシューティングの入口にすぎず、すべての答えではありません。信頼性の高い処理方法は、HTTP ステータスコード、レスポンスボディ内の業務エラーコード、関連するレスポンスヘッダー、プラットフォームのドキュメント、リクエスト ID、サーバー側ログを組み合わせ、「API エラー」を観測可能で、復旧可能かつ保守可能なエンジニアリングの仕組みに変えることです。

参考資料