Rate limits
Each key, and each signed credential, has a limit in each rate class. The reference names every operation's class as its x-rate-class.
Per key
In each class, a key may make at most this many calls a minute. The minute starts with the key's first call in that class. Past the limit, a call is answered 429 rate_limited, with Retry-After: the seconds until the minute ends.
| Rate class | Calls a minute | Operations |
|---|---|---|
| read | 300 |
|
| write | 120 |
|
| bulk | 12 |
|
The OpenAPI document (GET /api/gap/v1/openapi) takes no key and counts against no key's limits.
The signature checkAt launch
POST /api/gap/v1/signature-check takes no key, so it is limited by address instead: at most 60 calls a minute from one address our edge vouches for (for IPv6, from its /64). Past that, it answers 429 rate_limited, with Retry-After. Its answers carry no RateLimit header.
Failed authenticationAt launch
Past 5 failed authentications a minute from one address (for IPv6, from its /64), a call from it whose key is missing or unknown, or whose signature doesn't verify, is answered 429 auth_failures_throttled instead of 401, with Retry-After. A call with a valid key is never answered this way, nor is one from an address many callers share, such as a proxy range or a private network.
The headers
Every 429 carries Retry-After: wait that many seconds before you call again.
At launchThe responses to every key and signed credential also carry RateLimit-Policy, the limit the call counted against (its quota q and its window w in seconds), and RateLimit, what is left of it (r calls, within the next t seconds), in the form of the IETF RateLimit header fields draft. After the first read of a minute:
RateLimit-Policy: "read";q=300;w=60
RateLimit: "read";r=299;t=60Backing off
- On a 429, wait the seconds in
Retry-After, then send the same request again. Send a write with the sameIdempotency-Key, so nothing is created twice. - To send many referrals or roster changes, use the bulk operations: a bulk call counts once, however many rows it holds.
- To follow your referrals, page through
GET /api/gap/v1/referralswithupdated_afterrather than reading each referral in turn: Pagination says how.