如何用 API 发送 WhatsApp 消息(Python 与 curl)
有时候只做号码校验还不够,你需要真正发送一条 WhatsApp 消息:一条引导文案、一条通知、一条客服回复,或一条交易提醒。WhatsApp 消息 API 通过普通的 HTTP 请求实现这些。你只需绑定一次 WhatsApp 账号,之后每次调用都带上得到的 sessionId。
本文用 curl 和 Python 走完整个流程:创建并绑定会话、发送文本和媒体、校验收件人、读取回复,以及如何让账号保持良好状态。请只在你本人拥有或已获授权的账号上使用,并遵守 WhatsApp 的服务条款。
工作原理
发送是基于会话的。一个会话对应一个已绑定的 WhatsApp 账号,流程是:
- 创建会话:发起登录,拿到会话
id。 - 绑定账号:在 WhatsApp 应用里扫描二维码,或输入 8 位配对码。
- 轮询会话:直到状态变为
CONNECTED。 - 发送消息:把
sessionId传给各个发送接口。
连接成功后,会话 id 会变成由绑定设备派生的稳定 ID,重启后可继续使用,不必重新绑定。
第一步:获取 API Key
注册后在控制台复制一个有效 Key。账户赠送免费点数,可以完整跑通整个流程。
请求的组织方式
API Key 放在 key,会话放在 sessionId。GET 和 DELETE 接口的其余参数都放在查询字符串里;POST 接口则把 JSON 内容作为字符串放进 body 字段。每个响应都被包了两层,形如 {"code": 200, "message": "success", "data": {"code": 200, "data": {...}}}:外层由平台返回,内层来自消息服务,它始终返回 HTTP 200,失败时在自己的 code、title、detail 里说明原因。下面这个 Python 小封装把 body 字符串和两层响应都藏起来,在任何失败时抛出异常,后面每一步都会用到:
import json
import time
import requests
API_KEY = "YOUR_API_KEY"
BASE = "https://api.opendata-api.com/api/project/whatsapp-messaging"
SUCCESS_CODES = {200, 201, 202, 204}
def call(method, operation, payload=None, **params):
"""Call one operation and return the innermost payload (dict, list or None)."""
params["key"] = API_KEY
kwargs = {"params": params, "timeout": 60}
if payload is not None:
kwargs["json"] = {"body": json.dumps(payload)}
resp = requests.request(method, f"{BASE}/{operation}", **kwargs)
if not resp.ok:
raise RuntimeError(f"{operation} failed: HTTP {resp.status_code} {resp.text}")
body = resp.json()
# Peel both envelopes: {"code": 200, "data": {"code": 201, "data": {...}}}
while isinstance(body, dict) and "code" in body:
if body["code"] not in SUCCESS_CODES:
raise RuntimeError(f"{operation} failed: {body}")
body = body.get("data")
return body
第二步:创建会话并绑定账号
create-session 支持两种登录方式,推荐用二维码登录:
curl -X POST "https://api.opendata-api.com/api/project/whatsapp-messaging/create-session?key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"body":"{\"mode\":\"qr\",\"deviceName\":\"My Bot\"}"}'
然后用会话 ID 轮询 get-session。登录等待期间,响应里会带有可扫描的 qrImage(base64 数据 URI)和 qrPageUrl,后者可以在浏览器里打开实时观察登录过程。在手机上打开 WhatsApp,进入「设置」,再进入「已关联设备」,选择「关联设备」并扫描二维码。
如果不方便扫码,可以改用配对码登录:发送 {"mode":"pairing","phoneNumber":"8613800138000","deviceName":"My Bot"},其中 phoneNumber 是完整的国际号码,只含数字,不带 + 和空格。之后状态响应会返回一个 8 位的 pairingCode。在手机上选择「关联设备」,再选「改为使用手机号码关联」,输入这个配对码即可。deviceName 是显示在「已关联设备」里的名称。
create-session 的响应把会话放在 data.data 里,内层 code 为 201。会话的标识是其中的 id 字段,之后每次调用都把它作为查询参数 sessionId 传入。
session = call("POST", "create-session", {"mode": "qr", "deviceName": "My Bot"})
# The identifier is the "id" field; "sessionId" is only the query-parameter name.
session_id = (session or {}).get("id")
if not session_id:
raise RuntimeError(f"no session id in the response: {session}")
deadline = time.time() + 180
while time.time() < deadline:
state = call("GET", "get-session", sessionId=session_id)
print(state.get("status"), state.get("qrPageUrl"))
if state.get("status") == "CONNECTED":
break
if state.get("status") in ("FAILED", "LOGGED_OUT"):
raise RuntimeError(f"login failed: {state}")
time.sleep(3)
else:
raise TimeoutError("account was not linked in time")
可能出现的状态有 CREATED、WAITING_FOR_SCAN、CONNECTING、CONNECTED、DISCONNECTED、LOGGED_OUT 和 FAILED。
第三步:发送文本消息
会话状态变为 CONNECTED 后,调用 send-message,用 to 指定收件人,用 text 写入内容。这里有个几乎人人都会踩的坑:to 是完整的国际号码,只含数字、不带 +,并且必须是 JSON 数字而不是字符串。
curl -X POST "https://api.opendata-api.com/api/project/whatsapp-messaging/send-message?key=YOUR_API_KEY&sessionId=SESSION_ID" \
-H "Content-Type: application/json" \
-d '{"body":"{\"to\":8613800138000,\"text\":\"Hello from the API!\"}"}'
sent = call("POST", "send-message", {"to": 8613800138000, "text": "Hello from the API!"}, sessionId=session_id)
print(sent) # id, timestamp and to
响应会返回消息 id、timestamp 和 to。如果想以引用回复的方式发送,加上 quoteMessageId,值取自收件箱或聊天记录里的消息 ID。
第四步:先校验收件人
给一个没有 WhatsApp 的号码发消息,既浪费调用,也会让账号看起来可疑。check-contact-exists 借助你已连接的会话,告诉你某个号码是否有可用的 WhatsApp 账号,响应里包含 exists、该联系人的 jid 以及你传入的 query。号码通过 phoneNumber 传入,同样只含数字。
info = call("GET", "check-contact-exists", sessionId=session_id, phoneNumber="8613800138000")
print(info.get("exists"), info.get("jid"))
如果是对整份名单做校验,不依赖会话的查询更合适,请阅读如何检测号码是否在用 WhatsApp、WhatsApp 号码查询指南以及批量验证 WhatsApp 号码。
第五步:发送媒体
send-media 用 to 指定收件人(同样是 JSON 数字),type 可选 image、video、audio、voice、document 或 sticker,文件内容用 base64 编码后放进 data。voice 表示发送按住说话的语音消息。发送 document 时必须提供 fileName;caption 用来添加说明文字,mimeType 可覆盖自动推断的类型,seconds 用来设置音频或视频的时长。
import base64
with open("receipt.pdf", "rb") as fh:
data = base64.b64encode(fh.read()).decode("ascii")
call("POST", "send-media", {
"to": 8613800138000,
"type": "document",
"data": data,
"fileName": "receipt.pdf",
"caption": "Your receipt",
}, sessionId=session_id)
第六步:读取回复
list-inbox 返回会话在线期间实时收到的消息,最新的在前。每条记录包含消息 ID、chatJid、发送者、类型、文本和时间戳。收件箱每个会话最多保留最近 500 条,重启后会被清空,所以请轮询它,并把需要的内容复制进自己的数据库。用 limit 限制每次返回的条数,再用 mark-read 传入 chatJid 和 messageIds 发送已读回执。
messages = call("GET", "list-inbox", sessionId=session_id, limit=50) or []
for msg in messages:
print(msg["id"], msg["chatJid"], msg.get("text"))
错误处理与会话管理
- 要看内层
code,不能只看 HTTP 状态码。 消息服务即使拒绝了请求也会返回 HTTP 200,结果放在内层的code里,并附带title和detail,call()会据此抛出异常。平台层的401表示 API Key 缺失或无效,403表示点数不足或账户未启用。 - 重试之前先检查会话。 如果发送开始失败,先调用
get-session。账号只是离线时,这个调用会帮你重新连上。 - 不要盲目重试发送。 发送接口没有幂等键,超时后请先查看聊天记录,再决定是否重发。
- 有意识地断开连接。
disconnect-session只断开实时连接、保留已保存的登录。加上logout=true会彻底退出 WhatsApp,之后账号需要重新绑定。
请负责任地使用
未经许可的批量消息违反 WhatsApp 的条款,发送对方不想收到的消息的账号有被限制的风险。为了保持账号状态良好:
- 只发给有预期的人:已授权的用户、客户和交易通知的接收者。
- 不要群发冷名单。 先校验号码,只联系可触达且已同意的人。
- 控制发送节奏,加上间隔,避免向很多人发送完全相同的文本。
- 遵守 WhatsApp 的条款,以及适用于你业务场景的消息与隐私法规。
如果你的受众同时也在用 Telegram,同样的做法见用 Python 发送 Telegram 消息和 Telegram 登录教程。想决定每个联系人该走哪个渠道,请按线索核验工作流来做。
常见问题
怎么通过 API 发送 WhatsApp 消息?
先用 create-session 创建会话,通过扫描二维码或输入配对码绑定账号,等 get-session 返回 CONNECTED 之后,再带上 sessionId 调用 send-message,用 to 指定收件号码,用 text 写入内容。
为什么 send-message 拒绝我的号码?
to 必须是完整的国际号码,只含数字、不带加号,并且要以 JSON 数字而不是字符串的形式发送。例如 8613800138000,而不是 "+86 138 0013 8000"。
每次都要扫二维码吗?
不用。首次绑定之后,会话 id 会变成由设备派生的稳定 ID,重启后依然有效。已保存但离线的账号,调用 get-session 或 connect-session 就能恢复。logout=true 会删除已保存的凭据,所以之后需要重新绑定。
能接收到来的 WhatsApp 消息吗?
可以。list-inbox 返回会话在线期间收到的消息,最新的在前,每个会话最多保留最近 500 条。重启后收件箱会被清空,需要的内容请自行保存。
可以用来做批量营销吗? 请把它当作处理对方预期之内的对话的工具。未经请求或重复内容的批量消息会让账号面临风险,所以只发给已同意的联系人,先校验号码,并且把发送量控制在合理范围内。
立即体验
在 WhatsApp 消息页面浏览完整接口集,并阅读 create-session、send-message 与 list-inbox 接口文档。查看定价了解每次请求的成本。