Learning objectives
After reading this article you will be able to:
- Understand the three kinds of limits: per route, global and invalid requests
- Read the headers and body of a 429 response
- Fix and prevent rate limits in discord.js and discord.py
You check your bot's console and see this:
{
"message": "You are being rate limited.",
"retry_after": 1.337,
"global": false
}That is the body of an HTTP response with status 429 Too Many Requests. Discord limits how many calls a bot can make to its API, and yours went over. Most of the time your library already waits it out and the bot keeps working.
The serious case is when the bot ignores the warning, piles up error responses and Discord blocks the IP for a while. That temporary block is what Discord's own documentation calls a "Cloudflare ban".
What are Discord's rate limits?#
There are three separate mechanisms, and mixing them up causes most of the confusion.
- Per-route limit. Every endpoint has its own bucket. Sending messages in a channel has one limit, editing a channel has another. Values change, and Discord asks you not to hard-code them: read the headers.
- Global limit. Up to 50 requests per second per bot. Requests without an
Authorizationheader are limited per IP. Interaction endpoints (replies to slash commands) are not counted. - Invalid request limit. Responses with status 401, 403 and 429 count as invalid. Past 10,000 in 10 minutes, your IP is temporarily blocked. A 429 with
X-RateLimit-Scope: shared(a limit on a shared resource) does not count.
There is also a Gateway limit, on the WebSocket connection: 120 events sent per connection every 60 seconds. Go over it and Discord disconnects the bot immediately.
How do you read a 429 response?#
The headers that matter:
| Header | What it tells you |
|---|---|
Retry-After |
How long to wait before retrying |
X-RateLimit-Remaining |
Requests left in the bucket |
X-RateLimit-Reset-After |
Seconds until the bucket resets (may be fractional) |
X-RateLimit-Bucket |
Bucket ID, useful for grouping routes |
X-RateLimit-Global |
Present only when the global limit was hit |
X-RateLimit-Scope |
user, global or shared, only on 429 responses |
The JSON body has message, retry_after (seconds, fractional) and global. If global is true, the whole bot is paused, not just one route.
Why does my bot get 429s?#
From most to least common:
- Loops with no pause. Renaming 200 channels, giving a role to every member or deleting messages one by one, all at once.
- Constant message edits. A "live counter" or progress bar that edits every second hits the message route limit.
- Too many events triggering calls. A bot that replies to every message in a busy server, or answers each event with an API call.
- Blind retries. A call fails, the code retries immediately, and every attempt becomes another 429.
- Invalid token in a restart loop. The bot starts, gets a 401, crashes, restarts and repeats. Each cycle adds invalid requests. See the article on the bot token.
- Too many Gateway reconnections. The limit is 1,000 IDENTIFY calls per 24 hours. Hit it and Discord ends the bot's sessions and resets the token.
How do you fix a 429 error?#
In discord.js (v14)#
The library already waits out each rate limit. Your job is to watch for limits and cut volume. This snippet logs every limit hit and warns when invalid responses pile up:
import { Client, GatewayIntentBits, RESTEvents } from "discord.js";
const client = new Client({
intents: [GatewayIntentBits.Guilds],
rest: {
// warn every 500 401/403/429 responses within the 10-minute window
invalidRequestWarningInterval: 500,
},
});
client.rest.on(RESTEvents.RateLimited, (info) => {
console.warn(
`429 on ${info.method} ${info.route}: waiting ${info.timeToReset} ms (global: ${info.global})`,
);
});
client.rest.on(RESTEvents.InvalidRequestWarning, (info) => {
console.warn("Too many invalid requests:", info);
});
await client.login(process.env.DISCORD_TOKEN);The rejectOnRateLimit option (a function that receives the limit data and returns true, such as () => true, or a list of routes) makes the library throw a RateLimitError instead of waiting, useful for a command that would rather answer "try again shortly" than sit in a queue. Two defaults worth knowing: globalRequestsPerSecond is 50, Discord's global limit, and the invalidRequestWarningInterval warning is off (0).
In discord.py (2.x)#
discord.py also waits on its own, with no cap on the wait by default. You can set one with max_ratelimit_timeout (minimum 30 seconds, non-global limits only); beyond that, the library raises discord.RateLimited, with the wait in retry_after:
import asyncio
import os
import discord
intents = discord.Intents.default()
client = discord.Client(intents=intents, max_ratelimit_timeout=30.0)
async def rename_channels(channels: list[discord.TextChannel]) -> None:
for channel in channels:
try:
await channel.edit(name=f"{channel.name}-archive")
except discord.RateLimited as error:
print(f"Rate limited, waiting {error.retry_after:.1f}s")
await asyncio.sleep(error.retry_after)
await channel.edit(name=f"{channel.name}-archive") # retry once
await asyncio.sleep(1) # pause between batch items
client.run(os.environ["DISCORD_TOKEN"])What applies to any library#
- Respect
Retry-After. Do not retry early. If you make HTTP calls by hand, read the header and wait. - Process batches with a delay. A queue with a pause between items costs seconds and avoids the problem.
- Edit less. Refresh a dashboard message every 10 or 30 seconds, not every second.
- Use bulk endpoints when they exist. Deleting many messages has its own endpoint (bulk delete) instead of one call per message.
- Never retry 401 or 403. An invalid token or missing permission will not fix itself. Fix the cause.
What is the temporary Cloudflare ban?#
If your IP goes past 10,000 invalid requests in 10 minutes, Discord temporarily blocks that IP on the API. That works out to about 16 to 17 invalid requests per second, sustained, which happens with restart loops and retries without a pause, not with normal use.
If you suspect a block:
- Stop the bot. Every new attempt only feeds the counter.
- Find what produced the 401, 403 and 429 responses. Your logs show the route.
- Fix the loop and wait for the block to expire.
- Start the bot again with
invalidRequestWarningIntervalon, as in the example above.
How do you tell a rate limit from another problem?#
- 429 with a small
retry_after: a normal rate limit, the library handles it. - Repeated 429 with
global: true: you went past 50 requests per second. Look for the loop. - Repeated 401: wrong or reset token.
- Repeated 403: the bot lacks permission in that channel or role. Check before calling.
- Every call fails at once: possible IP block.
- Bot crashes and restarts non-stop: see why your bot goes offline.
How to do this on Square Cloud#
Rate limiting is a Discord API rule and applies on any host. What changes from one host to another is how early you spot the problem. On Square Cloud, logs stream in real time in the dashboard, the CLI and VS Code, so an invalid request warning shows up while the bot is running.
Automatic restart, optional and on by default when you deploy from the dashboard, brings the bot back when the process exits with an error. That keeps the bot up, but with an invalid token each restart can produce another 401, as in the loop described above. Handle the login error in your code: log a clear message, do not retry, and fix the token in the environment variables before restarting.
Discord bots run in isolated containers with discord.js, discord.py, JDA, Discord.Net or any other Gateway library, and the average latency to the Discord Gateway is around 30 ms. You deploy by ZIP upload, CLI, GitHub or the VS Code extension, and the token lives in the application's environment variables. Details are on the Discord bot hosting page. To get your first bot up, follow the guide on hosting a discord.js bot.
Bot hosting
Keep your bot online 24/7 with automatic restarts
FAQs
How long does a Discord 429 last?
The response tells you, in the Retry-After header and the retry_after JSON field, in seconds. Wait that long and retry.
What is Discord's global rate limit?
Each bot can make up to 50 requests per second to the REST API. Every route also has its own limit, which you read from the X-RateLimit-* headers.
Can a bot get banned for 429 errors?
Yes. Responses with status 401, 403 and 429 count as invalid requests, and 10,000 of them in 10 minutes get your IP temporarily blocked. A 429 with X-RateLimit-Scope set to shared does not count.
Do discord.js and discord.py handle 429 on their own?
Yes, both wait out rate limits by default. Trouble starts when your code generates too much volume, retries failing calls blindly or restarts in a loop with an invalid token.
More in Bots
See all 33 articles- How much does it cost to host a Discord bot?The cost of hosting a Discord bot depends on RAM, uptime, database and outside services. Learn how to estimate your own bill and where the money goes.
- Running a bot on your phone or PC vs hosting itA bot on your PC or phone (Termux) goes offline when the device shuts down, sleeps or loses its network. Compare the three options and when each one fits.
- How to host a Discord bot 24/7To keep a Discord bot online 24/7, run its process on an always-on machine with automatic restarts. Compare your PC, a VPS with pm2 or systemd, and bot hosting.
- Why your Discord bot keeps going offline (and how to fix it)A Discord bot goes offline when the process dies, the token is invalid, an intent is disabled or memory runs out. Learn to diagnose each cause and fix it.