Rate limits and quotas

Two limits on two clocks, both the account's. The headers that say where you stand, and what a 429 looks like.

Two limits

The burst is a number of requests a minute. It is counted per serving node, so with two nodes an account can briefly reach twice its number. That is accepted and said here rather than paid for with a shared counter.

The quota is a number of requests a UTC day. It is a hard cap: over it, every request answers 429 until midnight UTC, and nothing is ever billed for the excess. Tiers are flat prices.

Both belong to the account and are shared by every key of it. A request refused for either limit is not counted, so a client stuck on 429 does not spend its day on them.

The headers

HeaderClockMeaning
x-ratelimit-limitminuteThe tier's requests a minute.
x-ratelimit-remainingminuteLeft in this minute on this node.
x-ratelimit-resetminuteSeconds until the minute window ends.
x-quota-limitdayThe tier's requests a day.
x-quota-remainingdayLeft today, this request counted.
x-quota-resetdaySeconds until midnight UTC.
retry-aftereitherOn 429 only: seconds until the limit that refused you clears.

The tiers

Each tier's burst and quota are on the pricing page, read from the same table the API enforces, and on every response in x-ratelimit-limit and x-quota-limit.

What a 429 says

429 · the burst · retry-after: 23
{
  "errors": [
    {
      "code": "rate_limited",
      "detail": "The free tier allows 60 requests a minute"
    }
  ]
}
429 · the day · retry-after: 31260
{
  "errors": [
    {
      "code": "quota_exceeded",
      "detail": "The free tier's 1000 requests a day are used; the quota resets at midnight UTC. A higher tier is at https://immit.co/developers/pricing"
    }
  ]
}

Back off on retry-after. For the day's quota the honest answer is to stop until the reset, or to move up a tier, which applies at once.

Caching on your side

A keyed entry response is Cache-Control: private, max-age=3600 with an ETag, since a shared cache serving your hits would serve them unmetered. Send If-None-Match and a 304 costs you nothing to parse, though it still counts as a request. Cache entries on your own server by language and headword; the data changes slowly and updated_at says when an entry did. A client that caches rarely meets the quota at all.