WebAPIを設計するうえでの利用制限(Rate Limit)について調べた

# 経緯
APIを設計する時に「利用制限をどうすべきか」という議論が起きると思います。
とりあえず有名なAPIを調べて、傾向やビッグウェーブなどあればと思い調べました。

以下、各サービスのAPIドキュメントは2018年7月時点くらいに調べたものとなります。


# 結論（まとめ）
十人十色でした！！
言い換えると、共通のベストプラクティスという物は無さそうで
以下の2つの軸を元にサービスに応じてカスタマイズしていく形かなと考えます。

1. 制限をかける粒度を選択 / 組み合わせる
・サービス（大きな機能）単位で制限
・機能に応じて何種類かの区分を作り制限
・APIエンドポイント単位で制限
・重要度に分けて制限
・認証の種類で制限
 
2. 実行制限の枠（rate limit window）を決める
制限を掛ける＝リセットされるまでの、時間。

また、上記以外にもセットで[現在の利用制限状況の表現方法](#現在の利用制限状況の表現方法)を決める必要がありそうです。
特にリセットされるまでの表現について、後述で紹介するブログにも、非常に参考になる以下の記事にも記載がありますが、残り秒数にすべきという話があります。
参考：[翻訳: WebAPI 設計のベストプラクティス](https://qiita.com/mserizawa/items/b833e407d89abd21ee72#%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E5%88%B6%E9%99%90%E6%83%85%E5%A0%B1%E3%81%AF%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9%E3%83%98%E3%83%83%E3%83%80%E3%81%AB%E5%85%A5%E3%82%8C%E3%82%88%E3%81%86)



# 調べたAPIと結果
API制限といえばTwitterが有名かと思いますが、その他よく「イケてる」と耳にするAPIや、思いついたものを調べてみました。

- GitHub
- Google APIs
- kintone
- Twitter
- Slack
- Stripe


## GitHub
|制限時のHTTPステータス|参考URL|
|:--------|:-------------------------|
|403 Forbidden|[API Document](https://developer.github.com/v3/#rate-limiting)|
### 制限の一例
- search以外は1時間あたり5000req
- searchのみ1分30req（認証なしの場合は1分10req）

### 特徴
- searchとそれ以外でざっくりと定義されている。
- ただし、不正利用検知のようなロジックがあり、条件を満たすと同様に403を返すようになる（詳細のメッセージにその旨記載される）
-  HTTPレスポンスヘッダに制限状況が記載される。

## Google APIs
|制限時のHTTPステータス|参考URL|
|:--------|:-------------------------|
|403 FORBIDDEN|[API Document](https://developers.google.com/youtube/v3/getting-started?hl=ja#quota) <br>※リンク先はYoutube Data API|

### 制限の一例
- ※クォータという概念で管理される。

### 特徴
- 読み取りや書き取りなどリクエストによって消費されるクォータが異なる。
- APIの種類ごとにクォータが設定されており、[console](https://code.google.com/apis/console?hl=ja) より、[割り当て]から確認可能。


## kintone
|制限時のHTTPステータス|参考URL|
|:--------|:-------------------------|
|記載なし（200以外）|[API Document](https://developer.cybozu.io/hc/ja/articles/201941754-kintone-REST-API%E3%81%AE%E5%85%B1%E9%80%9A%E4%BB%95%E6%A7%98#step12)|


### 制限の一例
- APIによる同時接続数はドメインごとに100が上限。

### 特徴
- X-ConcurrencyLimit-Limitとして同時接続数の上限値を返却
- X-ConcurrencyLimit-Runningとして現在の同時接続数を返却

## Twitter
|制限時のHTTPステータス|参考URL|
|:--------|:-------------------------|
|429 Too Many Requests |[API Document1](https://developer.twitter.com/en/docs/basics/rate-limiting) <br>  [API Document2] (https://developer.twitter.com/en/docs/basics/rate-limits)<br>[ヘルプセンター](https://help.twitter.com/ja/rules-and-policies/twitter-limits) |

### 制限の一例
- 1日2400ツイート（※別途サービスに対しての制限もあり）。 <br> users/suggestions：15分あたり15回<br>

### 特徴
- 認証方法（ユーザー or アプリケーション）によってAPIエンドポイント単位で異なる制限が可能。
- 投稿などはユーザー単位のみの制限が適用される。
- HTTPレスポンスヘッダに当該APIの制限状況が記載される。
- プレミアムプランだと制限は別（無し？）というマネタイズもあり。

## Slack
|制限時のHTTPステータス|参考URL|
|:--------|:-------------------------|
|429 Too Many Requests|[API Document](https://api.slack.com/docs/rate-limits)|


### 制限の一例
- メッセージの投稿は1秒1回など
- 他、Web API methodsは1分あたり1～100回 <br>

### 特徴
- 制限の種類を5つのTierに分類し、APIごとにそのTierが指定されている。
- Retry-Afterヘッダで「再試行可能までの秒数」を返す

## Stripe
|制限時のHTTPステータス|参考URL|
|:--------|:-------------------------|
|429 - Too Many Requests|[ブログ](https://stripe.com/blog/rate-limiters) <br> [API Document](https://stripe.com/docs/api#errors)|

### 制限の一例
- ※具体的な閾値は言及なし

### 特徴
- 4種類の制限方法を組み合わせている。
    - Request rate limiter（毎秒Nリクエストの制限）
    - Concurrent requests limiter（同時実行の制御）
    - Fleet usage load shedder（クリティカルなAPIの一定利用枠の確保）
    - Worker utilization load shedder（重要度の低い順からリクエストを切っていく）



# 上限到達エラー時のHTTPステータスコード
Twitter,Stripe,Slackが「429 Too Many Requests」を返し、
GitHub, Google APIsは「403 Forbidden」を返すのが特徴的です。
429は[RFC6585](https://tools.ietf.org/html/rfc6585#section-4)に記載のあるやつですね。

# 現在の利用制限状況の表現方法
Twitter,GitHub,	kintoneは独自のHTTPヘッダにて返します。
一方、今回調べた中ではSlackが[rfc6585](https://tools.ietf.org/html/rfc6585#section-4)にも記載のあるRetry-Afterを用いています。

このあたりは2013年に言及された記事も見つけました。
➡[HTTPヘッダにUNIX Timestampを入れるべきではないのか](http://takaaki.info/2013/10/28/http-unix-timestamp/)



なお独自のHTTPヘッダは、各サービスごとに名称と意味合いが若干異なっておりました。
## Twitter
|HTTPヘッダ名|説明|
|:--------------|:-----|
|x-rate-limit-limit|そのエンドポイントの上限|
|x-rate-limit-remaining|ウィンドウ（15分）での残り要求可能数|
|x-rate-limit-reset|（※残り時間っぽい表現ですが、UNIX timeなのでリセット時刻では）|
## GitHub
|HTTPヘッダ名|説明|
|:--------------|:-----|
|X-RateLimit-Limit|1時間あたりに許可されるリクエストの最大数。|
|X-RateLimit-Remaining|ウィンドウでの残り要求可能数|
|X-RateLimit-Reset|ウィンドウでの制限がリセットされる時刻。|
## kintone
|HTTPヘッダ名|説明|
|:--------------|:-----|
|X-ConcurrencyLimit-Limit|同時接続数の上限値|
|X-ConcurrencyLimit-Running|現在の同時接続数|


# 所感
個人的にはGitHubの「サービス単位でシンプルな制限を課しつつ、別途不正利用検知という層を入れる」のが面白いなと思いました。<br>また、今回、そんなに沢山のサービスを調べた訳では無いですが、「ほぼ被っている」APIが1つも無かったことに驚きました。

RFCがーは理解しつつも、実際問題「あと1週間使えません」という時にClientに604800を返すのは険しいのではとも思ったのですが、そうした使い方は不正利用へのペナルティが目的となるはずで、今回のような話とは別に考えることなのだろうと思いました。
パフォーマンス維持を目的とするならRetry-Afterで足りるよう設計できるよね、と言われるとそうだなと思います。