Bot API reference
The 30 messages per second ceiling
A Telegram bot can send approximately 30 messages per second in total across all chats. The limit is enforced by the server with a 429 response and a retry_after value, it is not published as a hard number, and it applies to the bot as a whole rather than per recipient.
Broadcast rate limit: the exact figures
- Sustained send rate
- ~30 messages/second, bot-wide, across all chats
- Sustained rate
- ~30 messages/second, bot-wide
- Scope
- Per bot token, not per chat and not per process
- Server response
- HTTP 429 with a retry_after value in seconds
- 40,000 subscribers
- ~22 minutes at the ceiling, before failures
As of 2025-10-01, Telegram Bot API 13.4
What it means in practice
The practical consequence is arithmetic, and it surprises people. Forty thousand subscribers at thirty messages per second is twenty-two minutes of continuous sending, assuming nothing fails and nothing else is competing for the same budget. A promotion that has to reach everyone before an event starts cannot be triggered when the event starts.
The ceiling is bot-wide, which is the part most often missed. It is not thirty per chat, and it is not thirty per worker — spinning up ten instances of the same bot does not multiply anything, because the limit lives with the token rather than with the process. Everything the bot does shares the same budget: a broadcast running in the background will slow down replies to people actively in conversation, and the people in conversation are the ones who notice.
Telegram deliberately does not publish it as a fixed figure. The documented guidance describes roughly thirty per second as a safe sustained rate and warns that bursts may be tolerated briefly, which means a naive implementation appears to work at low volume and fails at the volume that matters. Treating the number as exact is a mistake in the other direction: the correct posture is to pace below it and to handle 429 as a normal event rather than an error.
The shape that works is a queue with a token bucket in front of it, persisted rather than held in memory. Each send draws a token; the bucket refills at a rate slightly under the ceiling; a 429 response pauses the bucket for the retry_after the server named rather than for a guessed interval. Because the queue is persisted, a worker restart mid-broadcast resumes rather than starting again — which matters more than it sounds, since restarting a partial broadcast means messaging the first several thousand people twice.
Scheduling deserves specific attention. Because the send takes real time, a reminder that must arrive at a precise minute cannot be dispatched at that minute. The queue is filled ahead of the deadline and paced so the last message lands on time, which means the design question is what window is acceptable rather than which instant is targeted. For appointment reminders a window is fine; for a market-open alert it usually is not, and that constraint belongs in the scope conversation rather than in a post-mortem.
Handling it in code
// Paced send with a token bucket and honest 429 handling.
const RATE_PER_SECOND = 25 // deliberately under the ~30 ceiling
let tokens = RATE_PER_SECOND
setInterval(() => { tokens = RATE_PER_SECOND }, 1000)
async function sendPaced(chatId: number, text: string): Promise<void> {
while (tokens <= 0) await new Promise((r) => setTimeout(r, 50))
tokens -= 1
try {
await bot.api.sendMessage(chatId, text)
} catch (error) {
// 429 is a normal event on a broadcast, not an exception. Honour the server's
// retry_after rather than a guessed backoff, then requeue this recipient.
const retryAfter = (error as { parameters?: { retry_after?: number } })
.parameters?.retry_after
if (retryAfter !== undefined) {
tokens = 0
await new Promise((r) => setTimeout(r, retryAfter * 1000))
return sendPaced(chatId, text)
}
throw error
}
}Questions this raises
Does running more instances of the bot raise the rate limit?
No. The budget belongs to the bot token, so ten workers share the same thirty per second and coordinate badly by default — each one believing it has the full allowance is how a broadcast trips 429 immediately. If you run multiple workers, the token bucket has to be shared state rather than a variable in each process.
Is 30 per second a documented hard limit?
It is documented as guidance rather than as a contract. Telegram describes roughly thirty per second as safe for sustained sending and notes that short bursts may be tolerated. Building against the exact number is unwise in both directions: pace under it, and handle 429 as a normal part of the flow.
What happens if I ignore the 429 and keep sending?
The retry_after values grow, and continuing to send through them is what escalates a temporary throttle into a longer restriction. The server is telling you precisely how long to wait; guessing a shorter interval is the single most common way a broadcast makes its own situation worse.
Can I get the limit raised?
For genuinely large broadcast use cases Telegram has historically been willing to discuss higher limits with bot owners, but it is not a self-service setting and it should never be assumed during scoping. Design for the standard ceiling; treat any increase as a bonus rather than a dependency.
Related limits
Sending into a single group is governed by a much lower and separate limit, covered in the per-group ceiling.
For what the server sends back when you exceed this, and how long it asks you to wait, see how flood waits actually behave.
How updates reach the bot in the first place, and why that choice affects throughput, is in webhooks versus long polling.
A production queue that survives a restart mid-broadcast is specified in the dispatch build.
Where the same ceiling is met with a channel post instead of one message per subscriber, see signal delivery.