Note
Telegram bot custom emoji not showing? Fixes
When a custom emoji “does not show”, the chat is usually showing the fallback character, or the API rejected the message. These checks are in the order that wastes the least time. The wording of the permission rule is from the Bot API, not from a guess.
1. The bot is allowed to send custom emoji
The formatting section of the Bot API says:
Custom emoji entities can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.
Read that as two cases.
- The bot bought additional usernames on Fragment.
- Or the message is sent directly by the bot to a private chat, a group, or a supergroup, and the owner of the bot has Telegram Premium.
The same sentence is repeated for custom emoji icons on keyboard buttons. If neither case is true, fix that before you touch offsets. The guide quotes the surrounding notes too.
There is a separate case for a supergroup’s own custom emoji set (custom_emoji_sticker_set_name on Chat). The docs say custom emoji from that set can be used by all users and bots in the group. That does not turn every ID in this directory into a free emoji.
2. You are looking at a forward or a notification
The HTML notes say a valid emoji must be the content of the tg-emoji tag. That emoji is what Telegram shows where a custom emoji cannot be displayed, for example in a system notification, and if the message is forwarded by a non-premium user. A plain fallback in those places is the documented behavior, not a broken ID.
3. Offset and length are wrong
offset and length are UTF-16 code units.
- The text
Hi ✅with a single BMP check mark: the emoji length is 1, the offset of the emoji is 3. ✔️is U+2714 plus U+FE0F, so the length is 2. The directory’s checkmark uses that character. See the ID page.- A normal flag such as Nigeria is 4 UTF-16 code units.
len("🇳🇬")in Python is 2, which is the wrong number to send. Count bytes ofutf-16-leand divide by 2.
The snippets on each ID page already use a message that starts with Hi , offset 3, and the length of that emoji.
4. The ID was parsed as a number
These IDs are larger than 9007199254740991. This ID:
5206607081334906820
becomes this if you run it through JavaScript Number:
5206607081334907000
The second value is not the emoji. Keep the ID quoted in JSON, Python, and Node.
5. Formatting modes are mixed
Use one of these, not a blend:
parse_modeHTML and a<tg-emoji emoji-id="ID">fallback</tg-emoji>tag, withentitiesomitted.- An
entitiesarray of typecustom_emoji, withparse_modeomitted. parse_modeMarkdownV2 and.
Legacy Markdown cannot express a custom emoji. The docs say so directly.
6. The ID is stale or mistyped
This directory was copied from public lists and was not re-checked live. Call getCustomEmojiStickers with the ID as a string. The method accepts at most 200 IDs. If Telegram does not return a sticker, the ID is not usable, whatever this site says.
To collect a pack that is not listed here, use fetch_pack.py and getStickerSet. The steps are on the guide.
7. The fallback itself is missing
The tag needs a real emoji as its content. The docs recommend the emoji from the sticker’s emoji field. That is the character shown on each card in the directory. An empty tag, or a letter where an emoji should be, is a different bug from a Premium limit.