Telegram's 1,024 and 4,096 limits count UTF-16 code units, not characters
If you write a Telegram bot or a posting tool, you have probably met the two numbers: 4,096 for a text message and 1,024 for a media caption. What the docs say less loudly is the unit. Telegram counts the length of the t
If you write a Telegram bot or a posting tool, you have probably met the two numbers: 4,096 for a text message and 1,024 for a media caption. What the docs say less loudly is the unit. Telegram counts the length of the text after formatting is removed, in UTF-16 code units, so most emoji take two units each. We at TGscope read 21,813,641 posts with text and looked at what real authors do near those walls. The full study is in our research on Telegram's character limit.
This post is the developer's version: how to count correctly in JavaScript, Python and SQL, how to split long text without breaking it, and what the data shows at the edge.
The limits
| Where | Limit | With Premium |
|---|---|---|
| Text message | 4,096 | 4,096 |
| Media caption | 1,024 | 4,096 |
| Poll question | 300 | 300 |
| Poll option | 100 | 100 |
Two details matter for code. The count is taken on the text without formatting, so bold markers, link targets and similar markup do not add to it. And the unit is a UTF-16 code unit, not a character a person would see.
Why "characters" is the wrong unit
UTF-16 stores every code point from the Basic Multilingual Plane in one unit and every code point above it in two (a surrogate pair). Most emoji live above the plane, so one emoji is two units. A caption made of emoji runs into the 1,024 wall at half the visible length for a sender without Premium.
The languages you are likely to use disagree about what "length" means:
const s = "launch \u{1F680}"; // the text "launch " and a rocket emoji
console.log(s.length); // 9, UTF-16 units: this is what Telegram counts
console.log([...s].length); // 8, code points
s = "launch \U0001F680" # the same text
print(len(s)) # 8, code points
print(len(s.encode("utf-16-le")) // 2) # 9, UTF-16 units
In JavaScript str.length is already correct. In Python len(s) is not, so encode to UTF-16 little endian (no byte order mark) and divide by two. If your text sits in DuckDB or a similar engine and length counts code points, add one for each code point above the plane. This is the expression we used on the whole corpus:
length(text)
+ length(regexp_replace(text, '[^\x{10000}-\x{10FFFF}]', '', 'g'))
The inner part keeps only the characters outside the plane, and the sum counts each of them twice. Count the text as Telegram will store it: if you build the message with HTML or Markdown entities, measure the plain text, not the markup.
What real posts look like
Most posts are far from the limits. The median text-only post is 183 characters long and the median caption is 250. Our sample covers the last 100 posts of 256,822 public channels, read from the public web version of each channel between October 26 and November 1, 2026.
Near the caption limit the picture changes. Exactly 1,024 characters is the length of 11,034 captions. At 1,023 there are 7,717, and the average over lengths 1,025 to 1,029 is 4,209. Authors without Premium write until the counter stops them and then trim to the limit.
The wall is soft for some people. 7.5% of captions are longer than 1,024, which is only possible with Premium. 45.1% of active channels with at least 10 captions posted at least one such caption. If your tool assumes 1,024 for every user, a good share of your users will hit your limit before Telegram's.
The text limit shows the same shape in smaller numbers. Exactly 4,096 characters is the length of 1,278 text posts, against about 382 per length just below. In our count 10,367 posts are longer than 4,096. That is a measurement artifact: the web version shows some formatting, such as hidden link targets or quotes, differently from the app, so our count can drift by a few dozen units. If you measure text scraped from the web page, expect the same.
Splitting long text safely
When a post does not fit, split it on a paragraph boundary, and never inside a surrogate pair. A cut between the two halves of an emoji produces a broken character on both sides.
function splitByParagraph(text, limit) {
const parts = [];
let cur = "";
for (const para of text.split("\n\n")) {
const next = cur ? cur + "\n\n" + para : para;
if (next.length <= limit) { cur = next; continue; }
if (cur) parts.push(cur);
cur = para;
while (cur.length > limit) {
let cut = limit;
const last = cur.charCodeAt(cut - 1);
if (last >= 0xd800 && last <= 0xdbff) cut -= 1;
parts.push(cur.slice(0, cut));
cur = cur.slice(cut);
}
}
if (cur) parts.push(cur);
return parts;
}
length and slice in JavaScript work on UTF-16 units, so the limit passed in is already in the right unit. The check on the last unit moves the cut back by one if it would land between a high and a low surrogate.
For captions the practical pattern is different. Send the media with a short caption that fits 1,024 and put the long text in a separate message, where the limit is 4,096 for everyone. That avoids the Premium question entirely.
Does length matter for reach
Authors often shorten posts for fear of losing readers. Inside a channel the data does not support that. We divided each post's views by the median of its own channel, so size and language cancel out. Posts with text get 1× of the typical views at every length, including posts over 2,000 characters. Posts without text get 0.98×. Length is a constraint of the platform, not a lever on views. How the other numbers of the corpus look is in the Telegram post statistics.
What this does not cover
The numbers describe public channels with a web page, the largest in our index, and the text as the web version renders it. Premium status is inferred from caption length, not observed. The limits themselves are Telegram's; if you work with the Bot API, check the current documentation before relying on any of them. Our other free checkers are listed on the TGscope tools page.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.