Telegram Bot API 发送消息教程:sendMessage 参数、格式、按钮与错误处理
Telegram Bot API 发送消息只需要调用一个方法:sendMessage。向 https://api.telegram.org/bot<token>/sendMessage 发送请求,带上 chat_id(发给谁)和 text(发什么,1–4096 个字符),Telegram(电报)就会以机器人的身份把消息投递到对应的聊天。本文可以当作 sendMessage 的中文文档来使用:先给出 curl、Python、JavaScript 的可复制示例,再讲 HTML 与 MarkdownV2 格式、内联按钮、群组和频道、静音发送与链接预览,最后整理最常见的错误。
Telegram Bot API 发送消息的请求格式
所有 Bot API 方法都通过 HTTPS 调用,地址格式为 https://api.telegram.org/bot<token>/METHOD_NAME。注意 bot 与 Token 之间没有斜杠;方法名不区分大小写;所有请求都必须使用 UTF-8 编码。支持 GET 和 POST 两种方式,参数可以通过以下四种方式传递:
- URL 查询字符串(query string);
application/x-www-form-urlencoded;application/json(上传文件除外);multipart/form-data(上传文件时使用)。
响应始终是 JSON:成功时为 "ok": true,结果在 result 中;失败时为 "ok": false,并带有 error_code 和 description,部分错误还会附带 parameters(如 retry_after、migrate_to_chat_id),方便程序自动处理。
sendMessage 参数中文说明
| 参数 | 类型 | 说明 |
|---|---|---|
chat_id | Integer 或 String | 必填。目标聊天的数字 ID,或 @channelusername 形式的用户名 |
text | String | 必填。消息正文,解析格式后为 1–4096 个字符 |
parse_mode | String | 格式解析模式:HTML、MarkdownV2 或旧版 Markdown |
entities | Array | 直接指定格式实体,可代替 parse_mode |
reply_markup | Object | 内联键盘、自定义回复键盘、移除键盘或强制回复 |
link_preview_options | Object | 链接预览的生成选项 |
disable_notification | Boolean | 静音发送,用户会收到无声通知 |
protect_content | Boolean | 禁止转发和保存消息内容 |
reply_parameters | Object | 以回复的形式引用某条消息 |
message_thread_id | Integer | 发送到论坛的指定话题 |
还没有 Token 或 chat_id?请先阅读 Telegram Bot Token 获取教程和 Telegram chat id 获取教程。另外请记住:机器人不能主动发起对话,用户必须先打开机器人并点击 Start。
用 curl 发送消息
这是最快的验证方式。GET 请求中的中文需要 URL 编码,用 -G 配合 --data-urlencode 可以让 curl 自动处理:
curl -G "https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/sendMessage" \
--data-urlencode "chat_id=123456789" \
--data-urlencode "text=你好,这是一条来自 curl 的消息"
更推荐用 POST 发送 JSON,按钮、预览选项等嵌套参数写起来更方便:
curl -X POST "https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/sendMessage" \
-H "Content-Type: application/json" \
-d '{"chat_id": 123456789, "text": "你好,这是一条来自 curl 的消息"}'
成功响应(节选):
{
"ok": true,
"result": {
"message_id": 42,
"chat": {"id": 123456789, "first_name": "Alex", "type": "private"},
"date": 1793266200,
"text": "你好,这是一条来自 curl 的消息"
}
}
记下返回的 message_id,之后可以用它编辑或删除这条消息。
用 Python 发送消息
使用 requests 库,封装成一个出错时抛出异常的函数:
import requests
TOKEN = "123456789:AAEexampleTokenReplaceWithYours"
API = f"https://api.telegram.org/bot{TOKEN}"
def send_message(chat_id, text, **params):
payload = {"chat_id": chat_id, "text": text, **params}
data = requests.post(f"{API}/sendMessage", json=payload, timeout=10).json()
if not data["ok"]:
raise RuntimeError(f'{data["error_code"]}: {data["description"]}')
return data["result"]
msg = send_message(123456789, "你好,这条消息来自 Python")
print("已发送,message_id =", msg["message_id"])
用 JavaScript(fetch)发送消息
适用于 Node.js 18 及以上版本(以 ES 模块运行,支持顶层 await),或任何提供 fetch 的运行环境:
const TOKEN = "123456789:AAEexampleTokenReplaceWithYours";
async function sendMessage(chatId, text, extra = {}) {
const res = await fetch(`https://api.telegram.org/bot${TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chat_id: chatId, text, ...extra }),
});
const data = await res.json();
if (!data.ok) throw new Error(`${data.error_code}: ${data.description}`);
return data.result;
}
const msg = await sendMessage(123456789, "你好,这条消息来自 JavaScript");
console.log("已发送,message_id =", msg.message_id);
这段代码应该运行在服务器端。不要把 Token 写进网页前端,任何人都能在浏览器里看到它。
parse_mode:HTML 格式
设置 "parse_mode": "HTML" 后,可以使用以下标签:
- 粗体
<b>、<strong>;斜体<i>、<em>;下划线<u>、<ins>;删除线<s>、<strike>、<del>; - 剧透(点击后才显示的文字)
<tg-spoiler>或<span class="tg-spoiler">; - 链接
<a href="…">; - 行内代码
<code>、代码块<pre>,指定语言时用<pre><code class="language-python">; - 引用
<blockquote>,可折叠引用<blockquote expandable>。
除了标签之外,文本中所有的 <、>、& 都必须写成 <、>、&。插入用户输入之类的动态内容前,一定要先转义:
import html
name = html.escape("Tom & Jerry <3", quote=False) # 结果:Tom & Jerry <3
send_message(
123456789,
f"<b>订单 #1024</b>\n收件人:{name}\n状态:<i>已发货</i>\n"
'<a href="https://example.com/track/1024">查看物流</a>',
parse_mode="HTML",
)
parse_mode:MarkdownV2 格式
MarkdownV2 的常用语法:*粗体*、_斜体_、__下划线__、~删除线~、||剧透||、[链接文字](https://example.com)、`行内代码`,三个反引号包裹代码块,> 开头表示引用。
最容易出错的是转义规则。在格式标记之外,下面这些字符都必须在前面加反斜杠 \:
_ * [ ] ( ) ~ ` > # + - = | { } . !
反斜杠本身也要转义。在 pre 和 code 内部,只需转义 ` 和 \;在链接的 (…) 部分,只需转义 ) 和 \。版本号里的点(如 v1.2)、日期里的横线、句末的感叹号,都是导致 400 Bad Request: can't parse entities 的常见原因。处理动态文本时用一个函数统一转义:
import re
def escape_md2(text):
return re.sub(r"([_*\[\]()~`>#+\-=|{}.!\\])", r"\\\1", text)
send_message(123456789, f"*版本 {escape_md2('v1.2-beta')}* 已发布\\!",
parse_mode="MarkdownV2")
const escapeMd2 = (s) => s.replace(/[_*[\]()~`>#+\-=|{}.!\\]/g, "\\$&");
用 reply_markup 添加内联按钮
内联键盘是显示在消息下方的按钮,按“行”组织。url 按钮点击后打开链接;callback_data 按钮点击后,机器人会收到一个 callback_query 更新,其中带有这段数据:
curl -X POST "https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/sendMessage" \
-H "Content-Type: application/json" \
-d '{
"chat_id": 123456789,
"text": "请选择:",
"reply_markup": {"inline_keyboard": [[
{"text": "打开网站", "url": "https://example.com"},
{"text": "点我", "callback_data": "pressed"}
]]}
}'
注意 callback_data 的长度限制是 1–64 字节,不是字符。一个汉字在 UTF-8 中占 3 个字节,所以最多只能放 21 个汉字,建议用简短的英文标识,例如 order:1024。用 JSON 发送时 reply_markup 是嵌套对象;用查询字符串或表单发送时,需要先把它序列化成 JSON 字符串。
用户点击回调按钮后,客户端会一直显示加载状态,直到机器人调用 answerCallbackQuery,所以即使不需要提示文字也要应答:
cq = update["callback_query"]
requests.post(f"{API}/answerCallbackQuery",
json={"callback_query_id": cq["id"], "text": "已收到"}, timeout=10)
发送到群组、频道和话题
- 群组:先把机器人加入群组,再使用群组 ID。普通群组的 ID 是负数,超级群组形如
-1001234567890。 - 频道:机器人必须是有发布权限的管理员。公开频道可以用
"chat_id": "@channelusername",私有频道需要使用-100开头的数字 ID。 - 论坛话题:在
chat_id之外再传message_thread_id,消息就会发到指定话题。
普通群组升级为超级群组后,ID 会发生变化。这时用旧 ID 发送会失败,错误响应中的 parameters.migrate_to_chat_id 就是新 ID,保存后重试即可。
静音发送、链接预览与回复消息
{
"chat_id": 123456789,
"text": "更新日志:https://example.com/changelog",
"disable_notification": true,
"protect_content": true,
"link_preview_options": {"is_disabled": true},
"reply_parameters": {"message_id": 42}
}
disable_notification 让用户收到无声通知,适合夜间推送;protect_content 禁止转发和保存。link_preview_options 取代了旧参数 disable_web_page_preview:用 is_disabled 关闭预览,或者用 url、prefer_small_media、prefer_large_media、show_above_text 调整预览的内容和位置。reply_parameters 中的 message_id 指定要回复的消息。
常见错误:400、403、429
请求失败时会返回 "ok": false,例如 {"ok": false, "error_code": 400, "description": "Bad Request: chat not found"}。
| 错误 | 原因 | 解决方法 |
|---|---|---|
400 Bad Request: chat not found | chat_id 错误、漏了负号或 -100 前缀;用户从未启动过机器人;机器人不在该群组或频道中 | 用 getUpdates 重新确认 ID;请用户点击 Start;把机器人加入聊天 |
400 Bad Request: can't parse entities | MarkdownV2 有字符没有转义,或 HTML 标签不合法 | 转义文本;临时去掉 parse_mode 定位问题 |
401 Unauthorized | Token 无效或已被撤销 | 用 getMe 检查 Token |
403 Forbidden: bot was blocked by the user | 用户屏蔽了机器人 | 停止向该用户发送,重试没有意义 |
429 Too Many Requests | 超过了频率限制 | 等待 parameters.retry_after 秒后重试 |
官方建议的发送节奏是:同一聊天每秒不超过 1 条,同一群组每分钟不超过 20 条,群发总量约每秒 30 条。遇到 429 时的最简重试逻辑:
import time
def send_with_retry(chat_id, text, **params):
while True:
payload = {"chat_id": chat_id, "text": text, **params}
r = requests.post(f"{API}/sendMessage", json=payload, timeout=10).json()
if r["ok"]:
return r["result"]
if r["error_code"] != 429:
raise RuntimeError(r["description"])
time.sleep(r.get("parameters", {}).get("retry_after", 1))
如果要给大量用户群发,仅靠重试是不够的,应该用队列控制发送速度。
在浏览器中试用 sendMessage
- 打开 Bot API Tester 中的 sendMessage,粘贴你的 Token(只保存在浏览器的 localStorage 中,工具界面为英文)。
- 填写
chat_id和text,按需设置parse_mode、reply_markup等参数。 - 点击执行或按 Ctrl+Enter,在 JSON 查看器中检查返回结果。
完整的参数列表可以查看英文版的 sendMessage 方法参考,所有方法的 OpenAPI 描述见 Swagger 文档。如果你刚开始接触机器人开发,建议从 创建 Telegram 机器人教程读起。
常见问题
Telegram Bot API 怎么发送消息?
向 https://api.telegram.org/bot<token>/sendMessage 发送 GET 或 POST 请求,带上 chat_id 和 text 两个必填参数。成功时返回 "ok": true,result 中是发送出去的 Message 对象。
机器人能主动给任何用户发消息吗?
不能。机器人只能给已经和它互动过的用户发消息:用户必须先打开机器人并点击 Start(或者发过消息)。否则 sendMessage 会报错,常见的是 400 Bad Request: chat not found。
MarkdownV2 需要转义哪些字符?
在格式标记之外,_ * [ ] ( ) ~ ` > # + - = | { } . ! 这些字符都要在前面加反斜杠 \,反斜杠本身也需要转义。漏掉一个就会返回 400 错误。
parse_mode 选 HTML 还是 MarkdownV2?
处理用户输入等动态文本时,HTML 更省心:只需要把 <、>、& 转义为 <、>、&。MarkdownV2 写起来简洁,但需要转义的字符很多,容易出错。
怎么用机器人往频道发消息?
先把机器人设为频道管理员并允许发布消息。公开频道的 chat_id 可以直接写 "@channelusername",私有频道则使用 -100 开头的数字 ID。
遇到 429 Too Many Requests 怎么办?
说明触发了频率限制。按响应中 parameters.retry_after 给出的秒数等待后再重试,并控制发送速度:同一聊天每秒不超过 1 条,同一群组每分钟不超过 20 条,群发总量约每秒 30 条以内。
相关教程
Telegram 机器人教程:从零创建 Telegram 机器人并发送第一条消息
Telegram 机器人教程:在 @BotFather 中用 /newbot 创建机器人并获取 Token,用 getMe 验证,再通过 getUpdates 找到 chat_id,用 curl 和 Python 发出第一条消息,附常见问题排查。
Telegram Bot Token 怎么获取:获取、查看、验证和撤销机器人 Token
Telegram Bot Token 怎么获取?用 @BotFather 的 /newbot 获取新 Token,在 /mybots 中查看已有 Token,用 getMe 检查是否有效,泄露后及时撤销,并配合 chat_id 接入 n8n 等工具。
Telegram Bot Webhook 设置教程:用 setWebhook 配置 Webhook URL 和 secret_token
Telegram Bot API setWebhook 中文教程:Webhook URL 的 HTTPS 与端口要求、secret_token 请求头校验、用 getWebhookInfo 排错、用 deleteWebhook 切回轮询,以及 409 等常见错误的解决方法。
Telegram chat id 获取教程:查看用户、群组、频道和话题的聊天 ID
Telegram chat id 获取教程:用 getUpdates 查看私聊、群组、超级群组、频道和论坛话题的聊天 ID,讲清负数和 -100 前缀的含义,并排查 result 为空、409 Conflict 等常见问题,附 Python 示例。