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.
| What | Limit | Applies to |
|---|---|---|
| Plain message content | 2,000 characters | content field (legacy messages) |
| Combined embed text | 6,000 characters across all embeds | Legacy embeds (max 10 per message, 25 fields each) |
| Components V2 text budget | 4,000 characters across all text-bearing fields | Every Text Display, label and option together |
| Total components | 40 (max 10 top-level) | Includes every nested component |
| Buttons per Action Row | 5 | A select menu takes the whole row |
| Select menu options | 25 | String select options per menu |
| Media Gallery items | 10 | Images/media per gallery |
| Button label | 80 characters | Longer labels are rejected, not truncated |
| Webhook username override | 80 characters | Per-message username field |
Rate limits and HTTP 429
Discord applies route and global rate limits. Responses can include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset-After and X-RateLimit-Bucket headers. An HTTP 429 response supplies Retry-After or retry_after in seconds. Read the values returned for your request instead of hardcoding a requests-per-second figure.
A rate limit is a delivery problem, not a reason to shorten valid message text. In DWEEB, stop retrying while the send countdown is active. If another tool uses the same webhook, coordinate its sending too. Use the webhook error guide when the failure has a different HTTP status or a field-specific validation error.
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 →