moymo,Docs
Skip to content
API Reference

Rate limits ​

Limits keep one misbehaving client from slowing everybody down. They work the way Discord's do, so a library that reads the headers works unchanged.

How they work ​

Every call belongs to a bucket: a kind of call on one channel or one server. Each bucket allows a number of calls in a window. Separate channels and separate servers have separate buckets, so a busy channel does not slow another. On top of the buckets, one application may make 50 calls a second in all.

Limits are counted per application, not per address: an application is the unit that is responsible, and a hosting provider's address is shared by strangers.

Headers ​

Every answer carries:

Header
X-RateLimit-LimitCalls allowed in the window of this bucket
X-RateLimit-RemainingCalls left in this window
X-RateLimit-ResetWhen the window ends, in seconds since 1970 (with decimals)
X-RateLimit-Reset-AfterSeconds until it ends
X-RateLimit-BucketA name for the bucket: calls with the same name share a limit

Over the limit the answer is 429 with Retry-After (whole seconds), X-RateLimit-Scope (user: your bucket; global: the 50-a-second limit) and a body:

Response429Too Many RequestsHTTP
http
HTTP/1.1 429 Too Many Requests
Retry-After: 2
X-RateLimit-Scope: user
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
X-RateLimit-Reset-After: 1.724
Content-Type: application/json

{
  "message": "You are being rate limited.",
  "retry_after": 1.724,
  "global": false
}

retry_after is in seconds and has decimals; Retry-After is the same time rounded up.

Wait, then try again
js
async function call(url, options, attempt = 0) {
  const response = await fetch(url, options)
  if (response.status !== 429 || attempt >= 5) return response

  const { retry_after } = await response.json()
  await new Promise(done => setTimeout(done, retry_after * 1000))
  return call(url, options, attempt + 1)
}
python
import time
import requests

def call(method, url, attempts=5, **options):
    for _ in range(attempts):
        response = requests.request(method, url, **options)
        if response.status_code != 429:
            break
        time.sleep(response.json()["retry_after"])
    return response

The buckets ​

CallsLimitPer
Create a message (POST /channels/{id}/messages)5 in 5 secondschannel
Edit or delete a message5 in 5 secondschannel
Read messages30 in 10 secondschannel
Reactions20 in 5 secondschannel
Typing5 in 10 secondschannel
Pins10 in 10 secondschannel
Bans and changes to members10 in 10 secondsserver
Creating, changing or deleting channels, roles, commands10 in 10 secondsserver or channel
Everything else30 in 10 secondsroute
All calls together50 in 1 secondapplication

The limits are in the response headers: do not hard-code the numbers, read the headers.

Other limits ​

WhereLimit
POST /api/v1/oauth2/token and /revoke60 per minute per IP address
A member using commands and buttons20 interactions in 10 seconds; autocomplete 30 in 10 seconds
Followups to one interaction10 in 10 seconds
Commands an application may register100 slash commands and 15 of each context-menu kind, per scope (details)
A bot's messages in all channels300 a minute, however the buckets are spread

Being a good citizen ​

  • Back off exponentially on 429 and 5xx. Honour Retry-After.
  • Do not poll for what the gateway (planned) will push.
  • Keep one token per bot process, and cache what you read.
  • Cache your OAuth2 tokens: an access token lasts an hour, so refresh once, not before every call.

The wiki is for Moymo's members and developers. Something wrong or missing? Use "Suggest a change" on any page.