Handling Discord API Rate Limits Gracefully
How Discord's per-route, global and invalid-request limits work, what gets bots temporarily blocked, and practical patterns to stay well under every limit.
On this page
Discord limits how fast bots can call its API. Most of the time your library handles this quietly, queueing requests until they’re allowed. But bots that ignore rate limits eventually hit the one limit libraries can’t save you from — and get temporarily blocked from the API entirely. This article explains the limits and how to design around them.
The three kinds of limits
Per-route limits
Each endpoint — sending a message to a channel, editing a member, adding a reaction — has its own bucket with a limited number of requests per time window. Buckets are often scoped to a resource, so sending messages to channel A and channel B are counted separately.
Discord tells you where you stand in response headers:
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
X-RateLimit-Reset-After: 1.2
X-RateLimit-Bucket: abcd1234
Discord deliberately doesn’t publish fixed numbers for most routes, because they can change. Well-written code reads the headers instead of hard-coding limits — and every major library does exactly that.
The global limit
Across all routes, a bot can make around 50 requests per second. Very large bots can request a higher limit from Discord. Interaction responses aren’t subject to the global limit, which helps command-heavy bots.
The invalid request limit
This is the dangerous one. If your bot makes too many invalid requests — responses with status 401 (unauthorised), 403 (forbidden) or 429 (rate limited) — in a short window, Discord’s edge temporarily blocks your IP from the API. The documented threshold is 10,000 invalid requests in 10 minutes. A block like this takes your bot fully offline until it expires, and no amount of retrying helps.
What a 429 means
When you exceed a limit, Discord responds with HTTP 429 and a retry_after value. Libraries wait that long and retry. A handful of 429s is normal on busy bots; a steady stream means your bot is fighting the limits and — because 429s count as invalid requests — edging toward a temporary block.
Log rate-limit events so you know when they happen. In discord.js:
const { RESTEvents } = require('discord.js');
client.rest.on(RESTEvents.RateLimited, (info) => {
console.warn(`Rate limited on ${info.route} for ${info.timeToReset} ms (global: ${info.global})`);
});
discord.py logs rate limits through the discord.http logger at warning level, so they appear in your console automatically.
What actually causes rate-limit trouble
In our experience, a few patterns cause most problems:
- Loops that send one request per item. Deleting messages one at a time, adding a role to every member individually, or DMing a list of users in a tight loop.
- Editing a message too often. Live-updating a status or progress message every second.
- Re-registering commands on every start. Especially painful with frequent restarts.
- Fetching instead of using the cache. Fetching the same member or channel repeatedly when the library already has it.
- Retrying forbidden actions. Repeatedly trying to message a user who has DMs closed, or edit a role above the bot’s own — every attempt is a 403, and 403s count toward the invalid-request limit.
That last one surprises people. A bot that keeps retrying things it isn’t allowed to do can get blocked without ever sending a single 429.
Patterns that keep you under the limits
Use bulk endpoints
Many operations have bulk versions:
- Bulk delete removes up to 100 messages (younger than 14 days) in one request —
channel.bulkDelete(100)in discord.js,channel.purge()in discord.py. - Bulk command registration replaces all commands in one call.
- Multiple role changes — setting a member’s full role list in one edit instead of adding roles one by one.
Batch your output
Instead of sending ten log messages, collect lines for a few seconds and send one message with ten lines, or one message with several embeds (up to ten per message).
Debounce edits
If a message shows live data, update it on an interval that makes sense for humans — every 15–30 seconds — and only when the content actually changed.
let pending = false;
function scheduleUpdate() {
if (pending) return;
pending = true;
setTimeout(async () => {
pending = false;
await statusMessage.edit(renderStatus());
}, 15_000);
}
Check permissions before acting
Before trying an action that might be forbidden, check whether the bot can do it. In discord.js, channel.permissionsFor(guild.members.me).has(...) and member.manageable answer most questions without an API call. Stop retrying DMs to users who have them closed — remember the failure and don’t try again.
Queue mass actions
For genuinely large jobs — assigning a role to 5,000 members — use a queue that processes items steadily and lets the library pace requests. Tell the user the job has started and report when it finishes, rather than holding an interaction open.
Trust your library
Don’t write your own retry-on-429 logic on top of a library that already handles it; you’ll double the retries and make things worse. If you make raw HTTP calls to Discord outside the library, read the headers and honour retry_after.
Gateway limits are separate
The gateway has its own limit on what your bot sends over the WebSocket: roughly 120 events per 60 seconds per connection. Most bots send very little — heartbeats, and occasionally a presence update or a request for guild members. Changing the bot’s status every few seconds is the usual way to hit this. Update presence rarely; once a minute is plenty for rotating statuses.
Identifying is limited too: each shard connection counts against a daily session-start allowance. A crash loop that reconnects over and over can exhaust it, which is another reason to fix crash loops quickly — see when and how to shard your Discord bot.
Shared IPs and dedicated IPs
The invalid-request block applies to the IP address making the requests. On most hosting, many bots share outbound IP addresses, which is normally fine — but it means another bot’s misbehaviour on the same IP could, in rare cases, affect yours. For larger bots where that risk matters, a dedicated IP removes it. On Kerit Cloud, the Supreme and Elite Discord bot plans include a dedicated IP along with a 99.99% uptime target.
Summary
Discord enforces per-route limits, a global limit of about 50 requests per second, and — most importantly — a limit on invalid requests that can temporarily block your IP. Libraries handle 429s for you; your job is to avoid creating them. Use bulk endpoints, batch messages, debounce edits, check permissions before acting, stop retrying forbidden actions, update presence sparingly, and log rate-limit events so you notice trouble before Discord does.