The short answer
- 1Every Bot API call is https://api.telegram.org/bot<token>/<method>; curl is enough to test it.
- 2getMe checks the token; sendMessage needs a chat_id and text.
- 3On failure read the description first, then the error_code.
In this article
All you need is the token
The Bot API works by sending HTTPS requests to fixed addresses. You do not need a library or a server to try it: a single curl line, or even the browser's address bar, is enough. The only thing you need is the token @BotFather gave you; see creating a bot with BotFather.
Every request has the same shape:
https://api.telegram.org/bot<token>/<methodName>
The token 123456:ABC-EXAMPLE and the numeric IDs on this page are examples. Use your own.
getMe: check the token works
Call this first. With a valid token it returns your bot's own details.
curl https://api.telegram.org/bot123456:ABC-EXAMPLE/getMe
The response looks like this (values are examples):
{
"ok": true,
"result": {
"id": 123456,
"is_bot": true,
"first_name": "Sample Helper",
"username": "sample_help_bot",
"can_join_groups": true,
"can_read_all_group_messages": false,
"supports_inline_queries": false
}
}
| Field | Meaning |
|---|---|
| ok | true on success; every response has it |
| result | What the method returned; for getMe, the bot's account |
| can_join_groups | Whether the bot can be added to groups (a @BotFather setting) |
| can_read_all_group_messages | false means it sees only commands and replies in groups; see bot privacy mode |
Real responses carry more fields depending on your bot's settings, and new ones appear as features are added. Write your code to ignore fields it does not know.
sendMessage: send something
Give it a destination, chat_id, and the content, text.
curl https://api.telegram.org/bot123456:ABC-EXAMPLE/sendMessage \
-d chat_id=987654321 \
-d text="Hello"
On success, result contains the message just sent: its message_id, the bot as from, the chat, the date in Unix time and the text.
Where chat_id comes from
A bot cannot message anyone first, so you get a chat_id from a message the user sent you. When someone taps Start or writes to your bot, the update your bot receives includes that chat's ID. How to receive updates is in webhooks vs long polling, and what the numbers mean, including negative IDs for groups, is in reading chat IDs. For a public channel where your bot is an admin, you can put @channelusername in place of the ID.
Reading a failed response
When a call fails, ok is false and you get a numeric error_code and a readable description. This is a real response from sending to a public channel the bot is not part of:
{
"ok": false,
"error_code": 403,
"description": "Forbidden: bot is not a member of the channel chat"
}
| error_code | Common cause |
|---|---|
| 401 | Wrong token, or an old token after you generated a new one |
| 400 | The chat does not exist or a field is malformed; the description says which |
| 403 | No right to send: the bot is not in the chat, or the user never started it or blocked it |
| 429 | Too many requests too fast; wait the number of seconds the response tells you, then retry |
Read the description before the number. Two different 403s have two different sentences. For the wider choice of APIs, see the four Telegram APIs.
FAQ
How do I test my Telegram bot token?
Call getMe: curl https://api.telegram.org/bot<token>/getMe. A valid token returns ok true and the bot's details.
Where do I get the chat_id for sendMessage?
From an update your bot receives after the user starts it or writes to it. Bots cannot message people first.
What does 'Forbidden: bot is not a member of the channel chat' mean?
Error 403: the bot is not in that channel, so it cannot post there. Add it as an admin first.
Sources
At a glance
- 1getMe
Confirms the token and shows the bot's settings.
- 2sendMessage
chat_id plus text; chat_id comes from the user's message.
- 3Errors
401 token, 400 input, 403 rights, 429 slow down.
Updated