Telegram Message Formatting

Telegram message formatting is the set of rules a bot has to follow to make text bold, italic, monospaced or linked. There are three ways to express it — MarkdownV2, HTML, and a plain string paired with a list of entity ranges — and the API accepts one per message. MarkdownV2 is the one most bots use and the one that fails most often, because it reserves eighteen characters and four of them appear in ordinary sentences: the full stop, the hyphen, the plus and the exclamation mark. A sentence as plain as Order #123 is ready! is already invalid, and Telegram answers can’t parse entities rather than explaining. This tool takes what you have — plain text, Markdown, or the answer a language model produced — and returns something the API accepts, in whichever of the three modes you send.

  • Escaping that knows where it is. Reserved characters are escaped by context, so the dots in pip install x==1.0 survive inside a code block instead of arriving full of backslashes.
  • Splitting that closes what it opens. Over 4,096 characters the message is cut on a boundary outside every entity, and the formatting is reopened in the next part.
  • Nothing leaves the tab. No upload, no account, and we never ask for your bot token — formatting text does not need one.

What this does that the others do not

Correct UTF-16 offsets
The API counts entity positions in UTF-16 code units. Python counts code points, so a single emoji shifts every later entity by one and the message is rejected. A JavaScript string index is already the right unit, so the numbers here come out right without a conversion step.
Entity-aware splitting
A plain slice(0, 4096) cuts through the middle of a bold run and the API answers Can’t find end of Bold entity. The break here is chosen to fall outside every entity.
Error decoding
Telegram reports the fault at a UTF-8 byte offset, which matches nothing your editor shows. Paste the error and get the line, the column and the character.

Each of the three is covered by tests that run on every commit, using the examples from Telegram’s own documentation.

Format a Telegram message

Paste your text or Markdown. Everything runs in this tab — nothing is uploaded, nothing is stored.

184 / 4,096 characters
This looks like output from a language model

Telegram accepts none of the document syntax below. It is being converted rather than escaped, so no stray ## or |---| ends up in the message.

  • line 1ATX heading (#) — Telegram has no headings
  • line 3**double asterisks** — MarkdownV2 uses a single one
  • line 5Bullet list — Telegram has no list markup
  • line 7Markdown link
  • line 9Markdown table — Telegram has no tables in text messages

How it will look

Rendered the way a Telegram client renders it. Spoilers stay hidden until you tap them.

Order #1284 is ready! Hi Priya, your order shipped today. • Tracking: IN-8842-XZ • Delivery: 2-4 days • Support: chat with us Item Qty ──── ─── Lamp 2 Reply STOP to unsubscribe.
12:00

What to send

The mode most bots use. Every reserved character is escaped by context, so code blocks stay readable.

*Order \#1284 is ready\!*

Hi *Priya*, your order shipped today\.

• Tracking: `IN-8842-XZ`
• Delivery: 2\-4 days
• Support: [chat with us](https://t.me/yourbot)

```
Item  Qty
────  ───
Lamp  2
```

>Reply STOP to unsubscribe\.

Send it

The string literal is escaped for the language, so it survives the paste.

from telegram import Bot
from telegram.constants import ParseMode

bot = Bot(token=BOT_TOKEN)
await bot.send_message(
    chat_id="@yourchannel",
    text="*Order \\#1284 is ready\\!*\n\nHi *Priya*, your order shipped today\\.\n\n• Tracking: `IN-8842-XZ`\n• Delivery: 2\\-4 days\n• Support: [chat with us](https://t.me/yourbot)\n\n```\nItem  Qty\n────  ───\nLamp  2\n```\n\n>Reply STOP to unsubscribe\\.",
    parse_mode=ParseMode.MARKDOWN_V2,
)

Decode an API error

Paste what Telegram answered. The offset in those errors counts UTF-8 bytes, which is why it never matches what your editor shows — this converts it back to a line and a column in the MarkdownV2 output above.

The conversion and escaping run on telegram-format-kit, an MIT-licensed package with the same tests this page is built on. Use it directly if you would rather do this in your own code.

Formatting text in the Telegram app

If you are writing a message by hand rather than sending one from a bot, you do not need any of the escaping below. Select the text and pick a style: on mobile, tap and hold to select, then choose from the pop-up; on desktop, select and use the right-click menu or a shortcut — Ctrl+B for bold, Ctrl+Shift+M for monospace. Telegram text formatting covers a fixed set of eight styles and no more: Telegram bold text, italic, underline, strikethrough, spoiler, monospace, code block and quotation. Typing the characters works too: *bold*, _italic_, ~strikethrough~, ||spoiler|| and `code`.

A bot cannot press a toolbar button, and that is the whole reason this page exists. It has to describe the formatting in the message it sends — as MarkdownV2, as HTML, or as a list of entity ranges — and each of those has rules that a person typing into the app never meets. One more thing worth saying plainly: Telegram has no font picker, and the “fancy font” generators you may have found substitute lookalike Unicode characters rather than changing any font. They break screen readers, they often fail to render on other devices, and search treats the result as gibberish. The eight styles above are the whole set.

Telegram markdown tables, lists and code blocks

An ordinary Telegram message has no list markup and no table markup, so anything that claims to send one is choosing a rendering on your behalf. This tool makes the choice explicit: bullets become the • character, numbered items keep their digits and get the escaped full stop MarkdownV2 requires, and a table becomes a single pre block with padded columns and a rule under the header — monospace is the only way alignment survives in a chat client. Code blocks are the one construct that maps directly: three backticks with an optional language name, which the app renders monospaced with its own copy button. Rich messages, added in Bot API 10.1 on 11 June 2026, finally have real list and table blocks; if you are targeting those, the rich message builder handles them.

Why naive escaping breaks code blocks

Most converters do one pass over the string and put a backslash in front of every reserved character. That is correct for prose and wrong everywhere else, because MarkdownV2 changes its rules depending on where you are. Inside a code or pre block only the backtick and the backslash are special; inside the bracketed target of a link only the closing parenthesis and the backslash are. Escape a code block with the prose rules and the user receives pip install python\-telegram\-bot==21\.0 — a command that no longer works if they copy it. This tool parses the message into ranges first and then decides each character’s escaping by which range encloses it, which turns the question from a guess into arithmetic.

Sending a language model’s answer to a chat

Wiring a model into a bot has become ordinary, and it produces a formatting problem that did not exist before. Models answer in full GitHub-flavoured Markdown: second-level headings, nested bullet lists, tables, horizontal rules, occasionally an image. Telegram supports none of those in a text message. Escaping them is the wrong instinct — it makes the message valid and leaves \#\# and \|---\| visible in the chat. Converting is the right one: a heading becomes a bold line, bullets become •, a numbered list keeps its numbers, a table becomes a monospace block with padded columns so it still lines up, and a horizontal rule is removed because there is nothing it could map to. The tool recognises that shape on paste and says so.

The mode that cannot fail to parse

If your messages keep getting rejected and the cause is never quite the same, stop escaping and send text plus entities. The text travels exactly as written, with the formatting described alongside it as a list of ranges, so there is no parse step to go wrong. The one hazard is the unit: those ranges are UTF-16 code units, and a language that measures strings differently will produce numbers that look right and land in the wrong place. That is the single most common bug in hand-written entity code, and it only shows itself once someone sends an emoji.

Telegram message formatting — questions

How do I format text in Telegram?
In the app, select the text and choose a style. On mobile, tap and hold to select, then pick Bold, Italic, Monospace, Strikethrough, Underline or Spoiler from the pop-up. On desktop, select and use the right-click menu or a shortcut such as Ctrl+B for bold and Ctrl+Shift+M for monospace. Typing the characters works too: *bold*, _italic_, ~strikethrough~, ||spoiler|| and `code`. A bot is a different matter — it cannot press a toolbar button, so it sends the formatting as MarkdownV2, HTML or entities, which is what the tool on this page produces.
How do I change the font style on Telegram?
Telegram has no font picker for messages, and the "fancy font" tools you may have seen do not change the font at all — they substitute lookalike Unicode characters, which break screen readers and often fail to render on other devices. What Telegram does offer is a fixed set of styles: bold, italic, underline, strikethrough, spoiler, monospace, code block and quotation. Those are applied per message from the selection menu, and they are the same set a bot can send through the API.
What is Telegram MarkdownV2?
MarkdownV2 is the second and current Markdown parse mode of the Bot API, passed as parse_mode when a bot sends a message. It replaced the legacy Markdown mode, which could not express underline, strikethrough, spoilers or quotations and did not allow nesting. The trade for that power is escaping: MarkdownV2 reserves eighteen characters that must each carry a backslash, and the rules change inside code blocks and link targets. That escaping is what this tool does for you.
How do I use ``` in Telegram?
Three backticks open and close a code block, and a language name can follow the opening set — ```python on its own line, the code, then ``` on a line of its own. The block renders monospaced with a copy button in the app. Two things trip people up: inside the block, backticks and backslashes still need escaping, and everything else must be left alone, so a converter that escapes dots and dashes inside code hands your reader a command that no longer runs. For a short fragment inside a sentence, single backticks give inline `code` instead.
Does Telegram support Markdown tables and lists?
Not in an ordinary message. There is no list markup and no table markup, so a converter has to choose a rendering: bullets become the • character, numbered items keep their digits, and a table becomes a monospace block with padded columns, which is the only way column alignment survives in a chat client. Bot API 10.1, released on 11 June 2026, changed this for rich messages, which do have real list and table blocks — the rich message builder on this site targets those.
Which characters have to be escaped in Telegram MarkdownV2?
Eighteen of them: _ * [ ] ( ) ~ ` > # + - = | { } . ! — each needs a preceding backslash. The four that catch people out are the full stop, the hyphen, the plus and the exclamation mark, because they appear in ordinary sentences: "Order #123 is ready!" is already invalid MarkdownV2 and the API answers Bad Request: can't parse entities. The rules change by context, though. Inside a code or pre block only the backtick and the backslash are escaped, and inside the bracketed part of a link only the closing parenthesis and the backslash are. That is why a single find-and-replace over the whole message corrupts code blocks — it escapes dots and dashes that were meant to stay.
Why does my bot answer "can't parse entities"?
Almost always one of three things. A reserved character was left unescaped, so Telegram started reading markup where you meant text. A marker was opened and never closed, which produces "Can't find end of Bold entity". Or the message was cut at 4,096 characters through the middle of an entity, leaving the closing marker in a part that was never sent. Paste the error into the decoder above with your output in place: the byte offset Telegram reports is counted in UTF-8, so it never matches what your editor shows, and the tool converts it back to a line and column you can act on.
How do I send ChatGPT or Claude output to Telegram?
Convert it rather than escape it. Language models answer in GitHub-flavoured Markdown — ## headings, bullet lists, tables, horizontal rules — and Telegram supports none of that in a text message. Escaping leaves the hashes and pipes visible in the chat; converting maps each construct onto something Telegram has: headings become bold lines, bullets become •, ordered lists keep their numbers, tables become a monospace block with padded columns, and horizontal rules are dropped because there is nothing to map them to. Paste the answer above and the tool detects that shape and converts it.
What is the difference between MarkdownV2, HTML and entities?
They are three ways to describe the same formatting. MarkdownV2 is the most used and has the most escaping rules. HTML mode escapes only three characters — <, > and & — which makes it easier to generate from a template. text + entities sends the plain text untouched alongside a list of ranges, so it has no escaping rules at all and cannot fail to parse; the catch is that the ranges are counted in UTF-16 code units, which is where most hand-written implementations go wrong. If your messages keep getting rejected and you cannot find why, entities is the mode that ends the problem.
Why are my entity offsets wrong when there is an emoji in the message?
Because the Bot API counts offsets in UTF-16 code units and your language probably does not. A Python len() counts code points, so an emoji outside the basic multilingual plane — which is most of them — measures 1 there and 2 to Telegram. Every entity after that emoji is then off by one, and the formatting lands on the wrong characters or the API rejects the message outright. JavaScript string indices are UTF-16 already, so the offsets this tool produces are correct as they stand. Copy them; do not recompute them on the other side.
How long can a Telegram message be?
4,096 characters for a text message and 1,024 for a media caption, counted after entity parsing — that is, on the text the reader sees, not on the escaped source you send. The distinction matters because escaping can nearly double the source: 200 full stops become 400 characters of MarkdownV2 but still count as 200. Rich messages, added in Bot API 10.1, raise the ceiling to 32,768. When a message does need splitting, the break has to fall outside every formatting entity, or the part ships with an unclosed marker.
Does my text get uploaded anywhere?
No. The parsing, escaping, conversion and preview all run in this browser tab, and the page makes no network request with your content. That is a design decision rather than a promise: people paste real broadcast copy and real customer names into a formatter, and there is no reason for any of it to reach a server. The same code is published as the MIT-licensed telegram-format-kit package if you would rather run it inside your own project.
Do you ever ask for my bot token?
Never, on this page or any other. Formatting is a pure text transformation — it needs no credentials to do its job, so asking for one would be collecting something we have no use for. If a formatting tool asks for your bot token, that is worth a second look: the token is full control of your bot.

Building the links your bot sends? The link generator builds t.me and tg:// pairs with start payloads. Working with Bot API 10.1 blocks instead? The rich message builder covers those. All of them are listed on the tools hub.

Telegram Message Formatting — Free Tool | Telegram.Software