- レート制限の主要アルゴリズム(固定ウィンドウ・スライディングウィンドウ・トークンバケット)の比較と選定基準
- SaaSの料金プランと連動したレート制限の設計方法
- 業界標準のレスポンスヘッダー(X-RateLimit-*)の実装
- 429 Too Many Requestsの適切なエラーレスポンス設計
- 分散環境でのレート制限とRedisを使った実装パターン
なぜAPIレート制限が不可欠なのか
レート制限は、APIの「安定性」と「公平性」を守る仕組みです。制限がなければ、1つのクライアントが大量のリクエストを送信することで、他のすべてのユーザーのサービス品質が低下するリスクがあります。
FUNBREWでは自社SaaSプロダクト「FUNBREW PDF」でAPIレート制限を設計・運用してきました。本記事では、その経験をもとにレート制限の実践的な設計パターンを解説します。
レート制限は後から追加すると、既存のAPIクライアントに影響を与えてしまいます。初回リリース時から適切な制限を組み込み、ヘッダーで制限情報を通知しておくことで、利用者に混乱を与えずに運用できます。
レート制限の設計では、以下の4つの要素を考慮する必要があります。
- アルゴリズムの選定:制限の計算方法
- プラン連動:料金プランに応じた制限値の設計
- ヘッダー設計:残量通知の標準化
- エラー処理:制限超過時の適切な応答
これらはAPIの認証設計と密接に関連しており、認証基盤の一部として設計することで一貫性のあるAPIを構築できます。
レート制限アルゴリズムの比較
レート制限の実装に使われる主要なアルゴリズムは3つあります。それぞれの特性を理解した上で、自社のAPIに最適なものを選びます。
1. 固定ウィンドウ(Fixed Window)
一定時間(例:1分間)ごとにカウンターをリセットする最もシンプルな方式です。
| メリット | デメリット |
|---|---|
| 実装が最も簡単 | ウィンドウ境界で一時的にバーストが発生 |
| メモリ使用量が少ない | 例:59秒目に100回 + 0秒目に100回 = 1秒間で200回通過 |
| 計算コストが低い | 公平性の観点では不十分な場合がある |
シンプルさが最大の利点ですが、ウィンドウ境界問題があります。制限が「100回/分」の場合、ウィンドウの終わりと始まりの境界で短時間に200回のリクエストが集中する可能性があります。
2. スライディングウィンドウ(Sliding Window)
現在時刻から過去N秒間のリクエスト数をカウントする方式です。
| メリット | デメリット |
|---|---|
| 境界問題が発生しない | 固定ウィンドウよりメモリを使用 |
| 制限が均一に適用される | タイムスタンプの記録が必要 |
| 利用者にとって直感的 | Redisのソート済みセットなどが必要 |
SaaS APIには最もバランスの良い選択肢です。固定ウィンドウの境界問題を解消しつつ、トークンバケットほどの実装の複雑さがありません。
3. トークンバケット(Token Bucket)
バケットにトークンが一定レートで追加され、リクエスト時にトークンを消費する方式です。
| メリット | デメリット |
|---|---|
| バースト許容と平均レートを両立 | 実装が最も複雑 |
| きめ細かい制御が可能 | パラメータ調整が難しい |
| 大手API(AWS等)で採用実績 | 利用者に仕組みの説明が必要 |
「バーストサイズ」と「補充レート」の2つのパラメータで制御でき、短時間のバーストを許容しつつ平均レートを制限できます。大規模APIサービスでは有力な選択肢ですが、小〜中規模のSaaSではスライディングウィンドウで十分なケースが多いです。
アルゴリズムの選定基準
| 要件 | 推奨アルゴリズム |
|---|---|
| とにかくシンプルに実装したい | 固定ウィンドウ |
| 公平性と実装コストのバランス | スライディングウィンドウ |
| バースト許容が必要な高トラフィックAPI | トークンバケット |
プラン別レート制限の設計
SaaSの料金プランと連動したレート制限は、ビジネスモデルの一部として設計します。
プラン別の制限値設計
制限値は「正当な利用には十分だが、濫用は防げる」レベルに設定します。
| プラン | リクエスト上限 | バースト許容 | 対象単位 |
|---|---|---|---|
| 無料 | 60回/分 | なし | APIキー |
| スタンダード | 300回/分 | 20回まで | APIキー |
| プロフェッショナル | 600回/分 | 50回まで | APIキー |
| エンタープライズ | カスタム | カスタム | テナント |
制限の適用単位
レート制限を「何に対して」適用するかも重要な設計判断です。
- APIキー単位:最も一般的。1つのAPIキーごとに制限を適用
- テナント単位:同一テナントの全APIキーで共有。エンタープライズ向け
- エンドポイント単位:負荷の高いエンドポイント(PDF生成、大量データ取得等)に個別制限を適用
- IPアドレス単位:認証前のリクエスト(ログイン試行等)に使用
実際の運用では、APIキー単位+エンドポイント個別制限の組み合わせが効果的です。全体のリクエスト数を制限しつつ、特に負荷の高い処理には追加の制限を設けます。
読み取り・書き込みの制限分離
読み取り(GET)と書き込み(POST/PUT/DELETE)で制限を分けることも有効です。
| 操作 | 制限の考え方 |
|---|---|
| 読み取り(GET) | サーバー負荷が軽いため、比較的緩めに設定 |
| 書き込み(POST/PUT/DELETE) | データ変更を伴うため、厳しめに設定 |
たとえば、読み取り300回/分、書き込み120回/分のように設定することで、データの参照は快適に行えつつ、書き込みの暴走を防げます。
業界標準のレスポンスヘッダー設計
レート制限の情報は、すべてのAPIレスポンスにヘッダーとして含めます。GitHub、Stripe等の主要APIが採用するX-RateLimit-*ヘッダーのデファクトスタンダードに沿った設計が推奨されます。なお、IETFではRateLimitヘッダーの標準化ドラフト(draft-ietf-httpapi-ratelimit-headers)が策定中です。
必須ヘッダー
| ヘッダー | 値の例 | 意味 |
|---|---|---|
| X-RateLimit-Limit | 300 | ウィンドウあたりのリクエスト上限 |
| X-RateLimit-Remaining | 247 | 残りのリクエスト可能数 |
| X-RateLimit-Reset | 1711805400 | 制限がリセットされるUNIXタイムスタンプ |
429応答時の追加ヘッダー
| ヘッダー | 値の例 | 意味 |
|---|---|---|
| Retry-After | 30 | 再試行までの推奨待機秒数 |
重要なのは、正常なレスポンス(200系)にもレート制限ヘッダーを含めることです。APIクライアントは残り回数を確認しながら、制限に達する前にリクエスト頻度を調整できます。
ヘッダー設計のベストプラクティス
- Resetの値はUNIXタイムスタンプで統一(ISO 8601形式との混在は避ける)
- Remainingは0以下にならないようにする(マイナス値はクライアントの混乱を招く)
- 読み取り・書き込みで制限が異なる場合は、該当する制限の値を返す
429 Too Many Requestsの設計
レート制限超過時のエラーレスポンスは、API利用者の体験を左右する重要な設計ポイントです。
エラーレスポンスの構造
429レスポンスには、利用者が自力で問題を解決できる情報を含めます。
- エラーコード:機械的に判別可能なコード(例:
rate_limit_exceeded) - メッセージ:人間が読める説明文
- 制限情報:現在の上限値と残り回数
- リトライ情報:何秒後に再試行できるか
- アップグレード案内:上位プランへの誘導(オプション)
リトライの設計指針
API利用者のクライアント実装をサポートするため、ドキュメントに推奨リトライ方式を明記します。
- Retry-Afterヘッダーを尊重:ヘッダーに指定された秒数だけ待つ
- 指数バックオフ:1秒→2秒→4秒→8秒と待機時間を倍増
- ジッター追加:ランダムな遅延を加え、複数クライアントの同時リトライを防止
- 最大リトライ回数:無限リトライを防ぐため上限を設定(例:5回)
「指数バックオフ+ジッター」の組み合わせは、AWSをはじめ多くのクラウドサービスが推奨するパターンです。
分散環境でのレート制限
マルチテナントSaaSでは、複数のアプリケーションサーバーでリクエストを処理するのが一般的です。分散環境でのレート制限には、共有ストアが不可欠です。
Redisによる実装パターン
Redisはレート制限の共有ストアとして最も広く採用されています。その理由は以下の通りです。
- 高速:インメモリ処理でマイクロ秒単位のレスポンス
- アトミック操作:INCRとEXPIREの組み合わせで競合を防止
- TTL(有効期限):ウィンドウのリセットを自動化
- ソート済みセット:スライディングウィンドウの実装に最適
スライディングウィンドウのRedis設計
Redisのソート済みセット(Sorted Set)を使ったスライディングウィンドウの設計は以下の流れになります。
- リクエスト受信時、現在のタイムスタンプをスコアとしてメンバーを追加
- ウィンドウ外(N秒前より古い)のメンバーを削除
- 残りのメンバー数をカウント
- カウントが上限以下ならリクエストを許可、超えていれば429を返却
- キー全体にTTLを設定し、不要なデータを自動削除
この方式では、各リクエストのタイムスタンプが個別に記録されるため、固定ウィンドウの境界問題が発生しません。
Redis障害時のフォールバック
Redisが一時的に利用できない場合の対応も設計しておく必要があります。
- フェイルオープン:Redisダウン時はレート制限を一時的に無効化し、リクエストを通す
- フェイルクローズ:Redisダウン時はすべてのリクエストを拒否する
一般的なSaaSではフェイルオープンを採用します。一時的にレート制限が効かなくなるリスクよりも、全ユーザーのAPIが使えなくなるリスクの方が大きいためです。ただし、フェイルオープン時はアラートを発報し、速やかにRedisを復旧させる運用体制が前提です。
モニタリングとアラート
レート制限は設定したら終わりではなく、継続的なモニタリングが重要です。
監視すべきメトリクス
| メトリクス | 確認ポイント |
|---|---|
| 429レスポンスの発生率 | 特定のAPIキーに集中していないか |
| 制限到達率 | 多くのユーザーが上限に達していないか(プラン設計の見直し指標) |
| Redis応答時間 | レート制限の処理自体がボトルネックになっていないか |
| 異常なリクエストパターン | 短時間での大量リクエスト、同一エンドポイントへの集中 |
制限値の継続的な調整
レート制限の値は固定ではなく、利用データにもとづいて継続的に調整します。
- 正当なユーザーの95パーセンタイルが制限に達していなければ、現在の設定は適切
- 多くのユーザーが制限に達している場合は、上限の引き上げかプランの見直しを検討
- 特定のエンドポイントで429が頻発する場合は、そのエンドポイントの最適化を優先
まとめ:実践的なレート制限設計の指針
APIレート制限の設計は、以下のポイントを押さえることで、セキュリティと利便性を両立できます。
- アルゴリズム選定:SaaSにはスライディングウィンドウがおすすめ
- プラン連動:料金プランごとに適切な制限値を設定し、アップセルの導線にする
- ヘッダー設計:業界標準の3ヘッダー(Limit・Remaining・Reset)を全レスポンスに含める
- エラー設計:429応答にはRetry-Afterと解決策を含め、利用者の自己解決を支援する
- 分散対応:Redisをバックエンドに、フェイルオープンで可用性を確保する
レート制限はAPI認証設計の一部として、プロダクトの初期段階から組み込むことを強くおすすめします。
FUNBREWでは、自社SaaSプロダクト「FUNBREW PDF」の運用で培った知見をもとに、システム開発をご支援しています。API設計やSaaS開発でお悩みの方は、ぜひお気軽にご相談ください。
この記事をシェア