DWEEB
Reference · Limits & rate limits

Discord Webhook Limits: Rate, Size and Components

Webhook failures usually trace back to one of three separate ceilings: the size of a single message, the speed you call one webhook, and how fast one channel accepts webhook deliveries. Knowing which ceiling you hit turns a mystery 400 or 429 into a five-minute fix.

Message size and component ceilings

The numbers below are the ones DWEEB validates against before send; the Components V2 rows are generated from the same constants the editor uses, so this table cannot drift from the product.

WhatLimitApplies to
Plain message content2,000 characterscontent field (legacy messages)
Combined embed text6,000 characters across all embedsLegacy embeds (max 10 per message, 25 fields each)
Components V2 text budget4,000 characters across all text-bearing fieldsEvery Text Display, label and option together
Total components40 (max 10 top-level)Includes every nested component
Buttons per Action Row5A select menu takes the whole row
Select menu options25String select options per menu
Media Gallery items10Images/media per gallery
Button label80 charactersLonger labels are rejected, not truncated
Webhook username override80 charactersPer-message username field

Rate limits and HTTP 429

Discord rate-limits per route: every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset-After headers describing the bucket you just spent from, and exceeding it returns HTTP 429 with a retry_after value. Those headers are the only contractual numbers — treat them, not any fixed figure, as the source of truth.

In practice, executing one webhook is bucketed at roughly five requests per two seconds, and Discord has additionally described a delivery cap of around 30 webhook messages per minute into a single channel. Both can change without notice, which is exactly why well-behaved senders react to the headers instead of hardcoding a rate.

HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-RateLimit-Remaining: 0

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

Staying under the limits

  • Send one rich Components V2 message instead of a burst of small ones — layout blocks replace the multi-message pattern.
  • Queue sends to a single webhook serially and sleep for retry_after (seconds) on any 429 before retrying.
  • Never fan a loop out over one webhook URL in parallel; the bucket is shared and every request after the first few will 429.
  • Schedule non-urgent posts instead of firing them together at the top of the hour.
  • Split genuinely long announcements by design (a follow-up message) rather than letting truncation decide.

How DWEEB enforces this before send

The editor tracks the character budget and component ceilings live, itemizes violations in the issue list, and blocks send on error-severity problems — so a 400 invalid form body for an oversized payload is caught before the request exists. The full nesting rules live in the Components V2 guide.

Put the guide into practice

Open the exact workflow in DWEEB. Nothing posts until you review and confirm it.

Validate a message against the limits →

Primary sources

Keep learning