Telegram API Send Message: sendMessage in curl, Python and Node

Telegram API Send Message

Telegram API Send Message: sendMessage and Connected Accounts

A code-first walkthrough of sending a Telegram message: the Bot API sendMessage endpoint in curl, Python and Node, the structural limit that stops a bot from writing to someone first, and how to send from a real connected Telegram account with Unipile's startChat and sendMessage.



send_message.sh
# Bot API sendMessage curl -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \ -d chat_id=123456789 \ -d text="Hello from the Bot API" \ -d parse_mode="Markdown"
200 OK: message sent
Decision Guide

Three ways to send a Telegram message, and which one to pick

There are exactly three ways to send a message through the Telegram API. They are not interchangeable: each one sends the message as a different kind of sender, with different constraints on who can receive it.
Bot API sendMessage
A single HTTP call, authenticated with a bot token from BotFather. The fastest way to send notifications, alerts and replies. The catch: a bot can only message users who have already started a conversation with it.
Best for bots and notifications
MTProto client (Telegram API)
Full protocol access via api_id and api_hash, used to build your own Telegram client from scratch (Telethon, Pyrogram, TDLib). The heaviest option to implement and maintain.
Best for building a custom client
Connected user accountUnipile
Sends as a real Telegram account, linked through Telegram's Devices feature (QR code or Hosted Auth). No "message me first" restriction, and it works for groups and existing conversations too.
Best for outbound messaging at scale
Getting a bot token or an api_id / api_hash pair is a separate topic. For that step-by-step, see our complete guide to getting Telegram API access. This guide only covers sending the message once you have credentials.


Build your Telegram integration
Code Tutorial

Send a message with the Bot API sendMessage endpoint

The Bot API exposes a single HTTP endpoint for sending a message: POST https://api.telegram.org/bot<TOKEN>/sendMessage. As of Bot API 10.2 (July 14, 2026), the required parameters are unchanged: a chat_id and a text body.
ParameterTypeDescription
chat_id RequiredInteger or StringUnique identifier of the target chat, or @username for a public channel.
text RequiredStringMessage text, 1 to 4096 characters after entity parsing.
parse_mode OptionalStringMarkdownV2, HTML or legacy Markdown, to render bold, links and code formatting.
disable_notification OptionalBooleanSends the message silently, without a push notification sound.
reply_parameters OptionalObjectSends the message as a reply to an existing message in the chat.



curl
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \ -H "Content-Type: application/json" \ -d '{ "chat_id": 123456789, "text": "Your order #4821 has shipped.", "parse_mode": "MarkdownV2" }'



send_message.py
import requests TOKEN = "123456:ABC-your-bot-token" url = f"https://api.telegram.org/bot{TOKEN}/sendMessage" payload = { "chat_id": 123456789, "text": "Your order #4821 has shipped.", "parse_mode": "MarkdownV2" } response = requests.post(url, json=payload) print(response.json())



sendMessage.js
const TOKEN = "123456:ABC-your-bot-token"; const url = `https://api.telegram.org/bot${TOKEN}/sendMessage`; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ chat_id: 123456789, text: "Your order #4821 has shipped.", parse_mode: "MarkdownV2" }) }); const data = await response.json(); console.log(data);
The Bascule Point

Why bots can't message first, and what that means for you

Every sendMessage call above works, right up until you try to reach someone who has never interacted with your bot. That's when the Bot API hits a wall that no parameter, retry or workaround fixes.
A bot cannot write to a user who hasn't written to it first
This is a structural rule of the Bot API, not a rate limit or a bug. Until a user sends /start (or any message) to your bot, sendMessage to that chat_id fails outright. This is fine for support bots and opt-in notification bots where the user reaches out first. It breaks entirely for outbound use cases: sales outreach, account recovery messages, transactional pings to a contact who has never opened your bot.
The MTProto client does not have this restriction: a full Telegram API client can message any user, the same way the Telegram app does.
A connected user account doesn't have it either, because the message is sent by a real Telegram account, not a bot. This is the approach covered in the next section.


Build the connected-account flow
Linked Account

Send from a real Telegram account with Unipile

Unipile connects an actual Telegram user account, not a bot. Linking happens through Telegram's own Devices feature, either a QR code scan or Hosted Auth with providers: "TELEGRAM". Because the message comes from a real account, there is no bot-only "message me first" restriction to work around. See the Telegram API product page for the full endpoint list.
1
Start a new conversation with startChat
When the recipient has no existing chat with the linked account, call POST /chats with user_ids and text. Telegram only allows one individual conversation per user pair, so if a chat already exists, this same call simply delivers the message into it instead of creating a duplicate.



start_chat.py
import requests url = "https://api6.unipile.com/v2/${accountId}/chats/send" headers = {"X-API-KEY": api_key} data = { "account_id": account_id, "user_ids": [telegram_user_id], "text": "Hi, following up on your request." } response = requests.post(url, headers=headers, data=data) chat = response.json() print(chat["chat_id"]) # recipient's ID for 1:1 chats
2
Send into an existing chat with sendMessage
Once a chat_id exists, call POST /chats/{chat_id}/messages with just the text body. This is the call to use for every follow-up message in an ongoing conversation.



send_message.js
const chatId = "CHAT_ID_FROM_STARTCHAT"; const form = new FormData(); form.append("text", "Your update is ready to review."); const response = await fetch( `https://api6.unipile.com/v2/${accountId}/chats/${chatId}/messages/send`, { method: "POST", headers: { "X-API-KEY": apiKey }, body: form } ); const sent = await response.json(); console.log(sent.message_id);
Both calls take multipart/form-data, not JSON. For 1:1 chats, the chat_id returned by startChat is the recipient's own ID: once you know a user's Telegram identifier, you can reuse it directly for sendMessage without a lookup step.


Start building with Unipile
Groups

Send a message inside a Telegram group

Sending into a group works the same way as sending into an existing chat: POST /chats/{chat_id}/messages with the group's chat_id and a text body. What changes is participant management, which uses its own endpoints.
OperationEndpointNotes
GET List participants/v2/{account_id}/chats/{chat_id}/participantsReturns the current member list of the group.
POST Add participant/v2/{account_id}/chats/{chat_id}/participantsRequires the "Add other members" permission to be enabled on the group.
DELETE Remove participant/v2/{account_id}/chats/{chat_id}/participantsThe linked account must be a group admin.
Check permissions before you rely on the response code. If the group doesn't grant "Add other members" or the linked account isn't an admin, the API can still return a success response without actually adding or removing anyone. Always verify the participant list after the call, don't trust the status code alone.
What's not supported for Telegram in Unipile
Channels, communities and broadcasts
Group admin actions: approving or promoting members
Contact, location, poll and event attachments
Chat archiving
Voice and video calls
Deliverability

Best practices to avoid Telegram restrictions

A linked account behaves like a real Telegram user, which means it is also held to the same anti-spam standards as one. Unipile's official guidance is direct: too many conversations started without a reply, or too many spam and blocking signals, can lead to a temporary restriction on the account.
Respect the delay
Never send less than 10 to 20 seconds apart between messages. Vary the interval, don't fire on a fixed clock.
Warm up new accounts
Avoid sending high volumes on freshly linked accounts. Start low and increase gradually as reply rates confirm the account is trusted.
Watch reply rates
A high share of conversations that get no reply is one of the strongest signals Telegram uses to flag an account.
Prefer warm outreach
Conversations that build on an existing relationship or referral generate fewer blocks than fully cold first messages.
This is a customer-side decision
Unipile relays the messages you send, it does not set your cadence for you. The sending volume, timing and pacing are decided by you, based on the account's history and Telegram's own tolerance at any given time. Building in realistic delays from day one is the single highest-leverage practice here.


Build with safe sending limits
Build With Unipile

Ready to send Telegram messages from real linked accounts?

One API for startChat and sendMessage across Telegram, WhatsApp, LinkedIn, Instagram, Gmail, Outlook and IMAP, plus Google Calendar and Outlook Calendar. Link an account and send your first message in minutes.

Telegram API Send Message - FAQ

Common questions about sending Telegram messages with the Bot API and with linked accounts.

Call POST https://api.telegram.org/bot<TOKEN>/sendMessage with a chat_id and a text parameter. The token comes from BotFather. The Bot API is free, and the current version is 10.2, published July 14, 2026.

Telegram requires the user to open the conversation with the bot before the bot can send anything. There is no endpoint that lets a bot start a chat with an arbitrary user. If your product needs to initiate contact, a bot is the wrong tool and you need a real user account.

startChat opens a new conversation and sends the first message in one call. sendMessage posts into a conversation that already exists. Telegram allows only one individual chat per user, so calling startChat on someone you already have a chat with simply posts into that existing chat.

With the Bot API, add the bot to the group and use the group chat_id. From a connected account, send to the group chat id like any other chat. Unipile also exposes participant listing, adding and removal on groups, but it does not support channels, communities or broadcasts.

Telegram supports MarkdownV2 and HTML. MarkdownV2 requires escaping a long list of reserved characters, so HTML is usually the safer choice when the message text is generated dynamically.

Do not use a brand new account purely for automation, ramp volume up gradually, and never send faster than one message every 10 to 20 seconds. Telegram bans permanently for flooding, spamming and inflating counters, and starting many conversations that go unanswered is a strong spam signal.

Still have questions? Our team is here to help.

Talk to an expert
en_USEN