· 6 min read

Using Telegram Bot API Message Entities for Precise, Structured Bot Output

Most Telegram bot guides focus on keyboards, callbacks, and sending plain text. A more overlooked feature is message entities: the structured annotations that let your bot format text precisely, build safer links, and generate richer messages without fragile manual markup.

Most Telegram bot development tutorials stop at sendMessage, inline keyboards, and a few formatting tags. That leaves one of the most useful parts of the Telegram Bot API underused: message entities.

If you build bots that generate dynamic text, notifications, command summaries, logs, alerts, or user-facing reports, entities give you a safer and more controllable way to structure output than plain Markdown strings or ad hoc HTML.

This guide focuses on what message entities are, why they matter, and how to use them effectively in real bot code.

What message entities are

A Telegram message can contain formatted spans of text called entities. Each entity describes a range of characters and a type, such as:

  • bold
  • italic
  • underline
  • strikethrough
  • spoiler
  • code
  • pre
  • text_link
  • text_mention
  • custom_emoji
  • blockquote
  • expandable_blockquote
  • url
  • mention
  • hashtag
  • bot_command
  • cashtag
  • phone_number
  • email

Instead of sending raw markup like *Hello* or <b>Hello</b>, you can send the text and a list of entity descriptors. Telegram will render the message based on those ranges.

This is especially useful when your message text is generated from user data, database values, or templates, because you can avoid parsing issues caused by escaping rules.

Why entities are better than Markdown in many bots

Markdown and HTML parse modes are convenient, but they come with tradeoffs:

  • You must escape special characters correctly.
  • Generated content can accidentally break formatting.
  • User-supplied input can create malformed markup.
  • Different parse modes have slightly different edge cases.

Entities avoid most of those problems because formatting is attached to character offsets, not embedded syntax. That makes them a better fit for bot-generated content.

A good rule of thumb:

  • Use parse modes for short, static messages.
  • Use entities for dynamic, structured, or data-driven messages.

Basic entity model

An entity needs three things:

  1. type — the formatting or semantic type
  2. offset — the start position in characters
  3. length — how many characters the entity covers

Example conceptually:

{
  "text": "Build completed successfully",
  "entities": [
    { "type": "bold", "offset": 0, "length": 5 }
  ]
}

Telegram will render Build in bold.

The most important detail is that offsets are based on characters, not bytes. This matters when your text includes emoji or non-Latin scripts.

The overlooked part: entities work well with dynamic composition

The feature many developers miss is that entities make it much easier to build messages from reusable parts.

Suppose you want to send a deployment notice:

  • environment name
  • version
  • status
  • link to logs
  • optional warning text

If you use Markdown, you need to concatenate a string carefully and escape every dynamic segment. With entities, you can assemble the final string first and then define formatting ranges afterward.

This pattern is especially helpful when:

  • combining multiple templates
  • adding or removing optional sections
  • localizing messages
  • formatting database-driven content
  • highlighting only certain values

Example: safe dynamic message generation

Imagine a bot that reports order status:

Order #1842
Customer: Ana Gómez
Status: refunded
Amount: $49.00

You might want the label names to be muted, the status to be bold, and the order number to be a clickable link.

With entities, the message can be created in a structured way:

text = "Order #1842\nCustomer: Ana Gómez\nStatus: refunded\nAmount: $49.00"
entities = [
    {"type": "bold", "offset": 0, "length": 11},
    {"type": "bold", "offset": 31, "length": 16},
    {"type": "bold", "offset": 49, "length": 13},
    {"type": "text_link", "offset": 6, "length": 4, "url": "https://example.invalid/orders/1842"}
]

In practice, you would compute offsets programmatically, but the idea is the same: keep the visible text and the formatting metadata separate.

When entities are especially valuable

1. Escaping user-generated content

If your bot includes user input in messages, parse modes can be risky.

For example, a user name like:

Alice _QA_

can break Markdown formatting if inserted directly.

Entities let you include the raw text safely and only apply formatting to trusted spans.

2. Data-heavy reports

Bots often send:

  • moderation logs
  • build reports
  • CI notifications
  • analytics snapshots
  • payment events

These messages tend to have repeating structure, and entities make them easier to maintain than custom markup strings.

3. Internationalized text

Localization often changes sentence order, punctuation, and spacing. If your formatting depends on exact markup tokens, translations can become fragile.

With entities, you can often format after translation or build entity ranges based on translated segments.

The text_link entity is an underrated alternative to writing Markdown links. It lets you keep the display text and URL separate.

That is useful when:

  • the URL is generated
  • the display label is localized
  • the link target contains query parameters
  • you want to validate URLs independently

Computing offsets correctly

Offsets and lengths are where many implementations go wrong.

Telegram expects these values in UTF-16 code units in some contexts and character positions in many libraries abstract this away for you, but the practical takeaway is: do not assume byte offsets. Unicode can make positions tricky.

Common pitfalls:

  • emoji count differently than ASCII
  • combining characters can alter visual length
  • languages with non-Latin scripts can break naïve indexing

Best practice:

  • use a Telegram library that helps compute entity offsets
  • avoid manual offset math unless you really need it
  • test with emoji, accents, and multilingual text

If your bot sends international text, this is not optional.

Building entities in code

Many Telegram libraries let you pass entities directly. The exact method varies, but the pattern is similar.

Python example

from telegram import Bot

bot = Bot(token="TOKEN")

text = "Deployment: prod\nVersion: 1.8.2\nStatus: successful"
entities = [
    {"type": "bold", "offset": 0, "length": 11},
    {"type": "bold", "offset": 17, "length": 7},
    {"type": "bold", "offset": 31, "length": 6},
]

bot.send_message(chat_id=123456789, text=text, entities=entities)

JavaScript example

await bot.sendMessage(chatId, text, {
  entities: [
    { type: 'bold', offset: 0, length: 11 },
    { type: 'bold', offset: 17, length: 7 },
    { type: 'bold', offset: 31, length: 6 },
  ],
});

In both cases, you are separating content from presentation.

Useful entity types beyond basic formatting

Many developers only use bold and italic. Telegram supports more.

Creates clickable text with an explicit URL.

Use this when you want the message to read naturally.

text_mention

Mentions a user without requiring an @username. This is useful when the user has no public username.

spoiler

Hides sensitive or surprise content until the user taps it.

Useful for spoilers, reveal-based UX, or privacy-preserving notices.

code and pre

Best for logs, stack traces, configuration snippets, and command output.

blockquote and expandable_blockquote

Useful for quoting messages, system notes, or collapsible explanatory sections.

These can make status messages much easier to scan.

A practical pattern: render first, annotate second

A clean architecture is:

  1. Build the final text.
  2. Track semantic spans while assembling it.
  3. Convert those spans into entities.
  4. Send message with entities.

This is much more robust than trying to inject markup into strings.

For example:

  • append label text
  • append a value
  • record the start/end of the value
  • add a bold or code entity to that value

This pattern scales well in large bots because it lets you centralize rendering logic.

Testing tips

Entity bugs often appear only with real data, so test with:

  • emoji: ✅, 🔥, 🧪
  • accents: José, García, Gödel
  • long strings
  • right-to-left text if relevant
  • empty optional fields
  • URLs with query parameters

Also test messages that mix several entity types in one send. Some library wrappers handle simple cases well but fail when entities overlap or nest in unsupported ways.

Constraints to remember

A few rules matter when designing entity-driven messages:

  • Not every entity can overlap with every other entity.
  • Some nesting combinations are invalid.
  • The message still has Telegram length limits.
  • Client rendering can differ slightly across platforms.

That means your renderer should fail gracefully if a complex annotation cannot be represented exactly.

When not to use entities

Entities are not always the best choice.

Avoid them when:

  • the message is a tiny static notice
  • your library has poor entity support
  • you need only one or two simple formatting tags and no dynamic data
  • your team is already standardized on safe HTML generation

For most production bots, though, entities are the stronger long-term option.

Recommendation for production bots

If you are building a serious Telegram bot, treat message entities as a core rendering tool, not a niche feature.

A good implementation strategy is:

  • define message builders that return {text, entities}
  • keep formatting logic separate from business logic
  • use library helpers for offset calculation if available
  • test with multilingual and emoji-heavy content
  • reserve parse modes for static hand-written text

That approach gives you safer output, fewer escaping bugs, and more flexibility as your bot grows.

Conclusion

Telegram Bot API message entities are easy to overlook because they do not look as immediately convenient as Markdown or HTML. But for real-world bots, they offer a cleaner and more reliable way to produce structured messages.

If your bot sends dynamic content, reports, links, or formatted user-facing text, learning to work with entities will save time and reduce bugs. It is one of those Telegram features that feels minor at first and becomes indispensable once you start building more serious automation.

← All posts