Zrzur Developers

Rate limits

How many requests a bot may send, how to read the headers, and what to do after a 429.

The three limits

  1. Buckets. Each route counts against a bucket, in a window of 10 seconds. The table below lists them.
  2. Global. A bot makes at most 50 requests a second, across all routes.
  3. Refused requests. One address that collects 10 000 answers of 401 or 403 within 10 minutes is blocked for 10 minutes. This stops token guessing; a healthy bot never gets near it.

Bots are counted by their id, so the limits are the same from any address and do not change with more processes.

The headers

Every response says where you stand. These values come from a real answer of GET /users/@me:

x-ratelimit-limit: 20
x-ratelimit-remaining: 13
x-ratelimit-reset-after: 3766
x-ratelimit-bucket: 7575212770586562519
HeaderMeaning
X-RateLimit-LimitRequests allowed in the window
X-RateLimit-RemainingRequests left
X-RateLimit-Reset-AfterMilliseconds until the window resets
X-RateLimit-BucketAn id of this bucket. It is a number, not the bucket's name
X-RateLimit-Globaltrue when the global limit was the one hit
Retry-AfterOn a 429 only: seconds to wait, rounded up

After a 429

A rate limit is a 429 without a type:

{ "retry_after": 3531, "global": false }

retry_after is in milliseconds.

let pausedUntil = 0;

async function call(path, init) {
  const wait = pausedUntil - Date.now();
  if (wait > 0) await new Promise((r) => setTimeout(r, wait));

  const response = await fetch(`https://api.zrzur.com${path}`, init);
  if (response.status !== 429) return response;

  const body = await response.json();
  if (body.type) throw new Error(body.type); // a refusal, see below
  if (body.global) pausedUntil = Date.now() + body.retry_after;
  await new Promise((r) => setTimeout(r, body.retry_after));
  return call(path, init);
}

A 429 with a type is a refusal to show to a person, not one to wait out. InSlowmode means the channel's slowmode, and RateLimited is a webhook's limit.

Buckets

Bucket/ 10 s
any_dlta20/webhooks, /, /applications, /oauth2, /custom, /invites, /soundboard
bots10/bots
channels15/channels (per channel)
default_avatar255/users
interaction_callbacks10/interactions (per interaction)
interactions15/interactions
interactions_autocomplete30/interactions
messaging10/channels (per channel)
servers5/servers (per server)
user_edit2/users (per user)
users20/users

/servers and /channels count per server and per channel. The servers bucket allows only 5 requests per 10 seconds per server, shared by reads and writes. Member lists (GET /servers/{target}/members) have their own bucket of 15, member_list. A busy moderation bot reaches it quickly: keep members in a cache fed by Ready and member events, and use the member data that comes with interactions.

/interactions/{interaction_id}/{token}/... has a bucket of 10 per interaction, which is plenty.

Other limits

WhatLimit
One webhook5 messages in 2 seconds
All webhooks of a channel30 messages a minute
Typing over HTTP5 per 10 seconds per channel
Interactions3 seconds to answer, 15 minutes to finish

Next