Telegram MarkdownV2 escaping: the complete reference
MarkdownV2 is the strictest of Telegram's three parse modes. It supports the most formatting, but any reserved character used as plain text must be escaped with a backslash — or the Bot API rejects the message with 400: can't parse entities.
The 18 reserved characters
Escape each of these with a preceding backslash in regular text:
_ * [ ] ( ) ~ ` > # + - = | { } . !Frequent offenders in real messages: . (every sentence), - (lists, ranges, compound words), !, # (issue numbers), + (phone numbers), ( ) (asides like this one).
Formatting syntax
| Style | MarkdownV2 syntax |
|---|---|
| Bold | *bold text* |
| Italic | _italic text_ |
| Underline | __underline__ |
| Strikethrough | ~strikethrough~ |
| Spoiler | ||spoiler|| |
| Inline code | `inline code` |
| Code block | ```python
code here
``` |
| Link | [label](https://example.com/) |
| User mention | [name](tg://user?id=123456789) |
| Custom emoji |  |
| Block quote | > quoted line |
| Expandable quote | **> hidden until expanded|| |
Entities can nest (bold inside italic, spoiler around links), but code and pre cannot contain other entities. Ambiguity trap: italic followed immediately by underline needs an empty separator — _italic_\r__underline__.
Context-specific rules
- Regular text: escape all 18 characters above.
- Inside
`code`and```pre```: escape only`and\. - Inside link/emoji URLs (the part in parentheses): escape only
)and\.
This context sensitivity is why one global escape function is not enough — it would corrupt your code blocks and URLs.
Escape functions
For plain-text segments only:
JavaScript
const escapeMdV2 = (text) =>
text.replace(/[_*[\]()~`>#+\-=|{}.!]/g, "\\$&");
const escapeCode = (text) => text.replace(/[`\\]/g, "\\$&");
const escapeUrl = (url) => url.replace(/[)\\]/g, "\\$&");Python
import re
def escape_md_v2(text: str) -> str:
return re.sub(r"([_*\[\]()~`>#+\-=|{}.!])", r"\\\1", text)
# python-telegram-bot ships one too:
# from telegram.helpers import escape_markdown
# escape_markdown(text, version=2)Or skip all of this
If your source text is standard Markdown (README files, LLM output, CMS content), converting beats escaping: telegramify-markdown in code, or the free md2tg converter in the browser or via HTTP from n8n and other automation tools.