# Discord rate limits, explained

> How the buckets work, which headers to read, why the global limit is not what most people think, and what actually gets you banned rather than throttled.

- Canonical: https://discordtools.blog/blog/discord-rate-limits
- Published: 2026-08-30
- Author: The Index desk
- Facts checked: 2026-08-30
- Sources:
  - Discord Developer Documentation — Rate Limits (https://docs.discord.com/developers/topics/rate-limits) — checked 2026-08-30
  - Discord Developer Documentation — Gateway (https://docs.discord.com/developers/events/gateway) — checked 2026-08-30
- Publication: The Index, an independent site not affiliated with Discord Inc.

---
Discord rate limits per **route and major parameter**, not per endpoint and not per application.
Getting that distinction right removes most of the confusion, and most of the accidental 429s.

## Buckets, not endpoints

A bucket is identified by the route plus its major parameter. The major parameters are
`channel_id`, `guild_id` and `webhook_id`.

So posting to channel A and posting to channel B are **separate buckets** with separate budgets.
Two requests to the same channel share one. This is why a bot can appear fine across a hundred
servers and get throttled hard in a single busy one.

Discord returns the bucket identity in `X-RateLimit-Bucket`. Two routes can share a bucket, and a
correct client keys its local limiter on that header rather than on the URL it constructed.

## Read the headers, do not hardcode the numbers

The published limits change. Anything you memorise from a tutorial will eventually be wrong, and
the response tells you the truth on every request:

| Header                    | Meaning                                                    |
| ------------------------- | ---------------------------------------------------------- |
| `X-RateLimit-Limit`       | Requests allowed in this window                            |
| `X-RateLimit-Remaining`   | Requests left before you are limited                       |
| `X-RateLimit-Reset-After` | Seconds until the window resets — prefer this over `Reset` |
| `X-RateLimit-Bucket`      | The bucket identity to key your limiter on                 |
| `X-RateLimit-Global`      | Present when you hit the global limit, not a route limit   |
| `Retry-After`             | On a 429, seconds to wait                                  |

Use `Reset-After` rather than `Reset`. `Reset` is an absolute timestamp and requires your clock to
agree with Discord's; `Reset-After` is a duration and does not.

## The global limit is per-token

The global limit — commonly cited as 50 requests per second — applies to your **bot token across
everything**, and it is separate from route limits. You can be well within every route bucket and
still hit it.

Interaction responses are exempt from the global limit. [Webhooks are limited per webhook](/blog/discord-webhooks-reference) and do
not count toward your bot's global budget, which is occasionally a useful property.

## The route limits that catch people out

Most routes behave as the headers describe. A few carry much tighter limits than their traffic
suggests, and they are the ones behind the majority of "my bot randomly stops working" reports.

**Channel name and topic edits: two per ten minutes, per channel.** This is the single most
surprising limit on the platform, and it is why member-count channels, live-status channels and
countdown channels stall. A bot updating a counter every minute succeeds twice and is then silent
for eight minutes — which looks exactly like a crashed bot. Any design that renames a channel on a
schedule must be built around this number, not discovered by it.

**Server-wide edits are slower than they look.** Bulk role syncs, mass nickname changes and
permission rewrites across many channels each consume their own bucket per target. Ten channels is
ten buckets, and firing them in parallel is the fastest route to a global limit.

**Bulk message delete has its own constraints** beyond rate limiting: it refuses messages older
than two weeks, and refuses a single-message batch. Both return errors that look like permission
faults and are not.

The general rule: if an operation changes something everyone can see, assume it is limited more
tightly than an operation that only posts a message.

## Invalid requests are the one that bans you

This is the part that surprises people, and it is the only rate limit with a real consequence.

Repeatedly sending requests that return **401, 403 or 429** is tracked separately. Exceed roughly
10,000 invalid requests in ten minutes and Cloudflare bans your IP for an hour — not a 429, an
actual block, and it takes your whole application down.

The practical implication: **a retry loop on a 403 is dangerous.** A 403 means you lack permission,
and retrying will never fix it. Treat 401 and 403 as permanent for that request, log them, and stop.
Only retry 429 and 5xx.

### Telling a Cloudflare block from a Discord 429

They arrive differently, and confusing them wastes hours.

A **Discord 429** is JSON, carries `retry_after` and the `X-RateLimit-*` headers, and is a normal
part of operation. A **Cloudflare block** returns an HTML error page with no `X-RateLimit-Bucket`
header and no JSON body — a client that assumes JSON will throw a parse error and report something
misleading.

If your error handler cannot parse the body of a 429, you are almost certainly banned rather than
throttled, and the fix is to stop sending entirely for an hour and find the source of the invalid
requests.

## Handling a 429 correctly

Read `Retry-After` and wait exactly that long. Do not retry immediately, do not use your own
backoff curve when the server has told you the number, and do not fire the retry from a different
worker that does not know about the wait.

If `X-RateLimit-Global` is present, pause **every** request from the token, not just that route.

A correct client keeps a per-bucket queue plus one global gate. That is more work than a naive
`sleep` on failure, and it is the difference between a bot that degrades under load and one that
falls over.

### The shape of a correct limiter

Four components, and skipping any one of them produces a specific failure.

**A queue per bucket**, keyed on `X-RateLimit-Bucket` from the last response for that route. Without
it, two concurrent requests to the same channel each believe they hold the last slot.

**A single global gate** the whole token passes through. Without it, a global 429 on one route is
ignored by every other route and the block extends.

**A shared store when you run more than one process.** Eight PM2 workers each holding a local
limiter believe they each have the full budget, so the effective request rate is eight times what
the limiter thinks. This is the most common cause of unexplained 429s at scale, and no amount of
per-process tuning fixes it.

**A hard stop on 401 and 403**, with the failure logged rather than retried.

## Gateway limits are different again

The gateway has its own budget: 120 events per 60 seconds per shard, and a separate limit on
identify calls that governs how fast a large bot can start up.

Presence updates are the usual culprit — a bot that updates its status frequently can exhaust its
gateway budget and start missing events, which looks like a bug somewhere else entirely.

**Startup is where sharded bots meet this.** The number of shards that may identify concurrently is
returned by the gateway-bot endpoint rather than being fixed, and the daily session-start allowance
is finite — so a bot in a crash-restart loop can exhaust its ability to connect at all and then
appear permanently offline for reasons unrelated to the crash. Read the values from the endpoint at
startup rather than hardcoding them, and back off on repeated connection failures.

## When you are on the other side of a bot's own API

Everything above is Discord's limit on a bot. A bot that itself publishes a developer API imposes a
second, independent limit on whoever calls it, and the same habits apply: read what is published, key
your client on the documented window, and treat a 429 as a signal to slow down rather than retry
immediately.

Vetox's [Vito public API](/blog/vetox-vito-reference) is a concrete example of the tiered-limit shape:
180 requests per minute and 5,000 per hour on a Silver or Gold membership, rising to 600 per minute
and 15,000 per hour on Platinum or Diamond. A single IP address is additionally capped at half the
project's per-minute budget, floored at 30 — the anti-abuse equivalent of Discord's own per-token
global limit above, scoped to the caller's network address rather than its credential. Only five of
the thirteen bot developer APIs examined for
[the API comparison on this site](/blog/discord-bot-api-comparison) publish their limit as a number at
all; most leave it to be discovered in production, which is the exact failure mode the rest of this
page exists to help you avoid.

## Observing your own rate-limit behaviour

You cannot tune what you do not measure, and three signals cover it.

**Log every 429 with its bucket and `retry_after`.** A single number — 429s per thousand requests —
tells you whether a change helped.

**Log the remaining count on a sample of requests.** A bucket that regularly reaches zero is one
burst away from throttling, and it shows up here before it shows up as a failure.

**Alert on invalid requests, not just on failures.** The count of 401s and 403s is the metric that
precedes an IP ban, and it is usually the only warning you get.

## What this means in practice

Most bots never think about rate limits until they do something bulk: renaming channels on a
schedule, syncing roles across a large member list, posting to many channels at once.

Three habits prevent nearly all of it. **Queue bulk work** rather than firing it in parallel.
**Key your limiter on the bucket header**, not the URL. **Never retry a 4xx that is not a 429.**

Bots that serve large numbers of servers handle this with a shared limiter across processes, so
that eight workers do not each believe they have the full budget. If you are running at that scale
and seeing unexplained 429s, a per-process limiter is the first thing to check.

## Frequently asked questions

**Why is my counter channel only updating twice per ten minutes?** Channel name and topic edits are
limited to two per ten minutes per channel. Design around it rather than retrying.

**Do webhooks count against my bot's global limit?** No — webhooks are limited per webhook and are
independent of the bot token's budget.

**What is the difference between a 429 and a Cloudflare ban?** A Discord 429 is JSON with
`retry_after` and rate-limit headers. A Cloudflare block is an HTML page with neither, and it lasts
an hour.

**Should I retry a 403?** Never. It means the permission is missing, retrying cannot fix it, and
repeated 403s count toward the invalid-request budget that ends in an IP ban.

**Why do I get 429s with a limiter in place?** Most often because each process has its own limiter.
A multi-process bot needs a shared one.