约定
请求接受 application/json 或表单编码的请求体;响应始终为 JSON。时间戳采用 UTC 的 ISO 8601 格式。金额是以美元计价、保留四位小数的十进制字符串。电话号码采用 E.164 格式并以加号开头。标识符带有前缀:msg_、bat_、evt_、cmp_。每个响应都包含 X-Request-Id;联系支持团队时请提供此值。
身份验证
Authorization: Bearer sk_live_9f2c...a71e权限范围:send(创建消息)、read(所有 GET 请求)。密钥可以设置 IP 允许列表;来自其他地址的请求会收到 401 unauthorized。测试密钥(sk_test_)的行为相同,但不会实际发送,也不会计费。
幂等性
任何 POST 请求都接受 Idempotency-Key,其值是由您指定、最长 128 个字符的字符串。24 小时内使用相同密钥和相同载荷时,会返回原始响应,并附带请求头 Idempotent-Replayed: true。相同密钥配合不同载荷时,会返回 409 idempotency_conflict。
创建消息
POST/v1/messages
| 参数 | 类型 | 说明 |
|---|---|---|
| to必填 | string | 采用 E.164 格式的目标号码,例如 +14155550142。不支持的号段会在发送前被拒绝。 |
| text必填 | string | 消息正文。系统会自动检测 GSM-7 或 Unicode;限制为每条消息最多 6 个分段。 |
| from | string | 发件人 ID:字母数字格式(3 至 11 个字符,可用 A-Z、0-9 和空格)或租用号码。省略时,平台会按目标地区选择。若线路有要求,系统会自动替换。 |
| send_at | datetime | 安排在某个 UTC 时刻发送。不能与 window 同时使用。 |
| window | object | { local_start, local_end, quiet_hours }。在收件人的本地时间窗口内送达;quiet_hours 可设为 default 或 off,事务性流量还可设为 bypass。 |
| validity | integer | 未送达消息在过期前的有效分钟数。默认值为 2880(48 小时),最大值为 4320。 |
| campaign | string | 最长 64 个字符的自由标签,用于在报告和 Webhook 中将消息分组。 |
| reference | string | 您自己的标识符,最长 128 个字符,会原样回显在响应和 Webhook 中。 |
| webhook_url | string | 仅为此消息覆盖账户端点。必须使用 HTTPS。 |
POST /v1/messages
Authorization: Bearer sk_live_...
Idempotency-Key: order-4821-ship
Content-Type: application/json
{
"to": "+14155550142",
"from": "METEOR",
"text": "Your order #4821 has shipped. Track it: smsmtr.io/t/8fK2",
"campaign": "shipping",
"reference": "order-4821"
}{
"id": "msg_9Kd2fQ",
"status": "queued",
"to": "+14155550142",
"from": "METEOR",
"text": "Your order #4821 has shipped. Track it: smsmtr.io/t/8fK2",
"segments": 1,
"encoding": "gsm7",
"price": "0.0084",
"currency": "USD",
"country": "us",
"campaign": "shipping",
"reference": "order-4821",
"created_at": "2026-09-21T11:42:07Z",
"sent_at": null,
"delivered_at": null,
"carrier_code": null
}创建批量消息
POST/v1/messages/batch
每次 API 请求最多 1,000 条消息。每条消息都有自己的 to、text,以及可选的 from、reference。顶层的 from、campaign、send_at、window 和 pace 会应用于所有未覆盖这些值的消息。系统会根据语法以原子方式接受或拒绝整个批次;单条消息仍可能因目标号码或内容而被拒绝,并会列出具体原因。
| 参数 | 类型 | 说明 |
|---|---|---|
| messages必填 | array | 消息对象列表,包含 1 至 1,000 项。 |
| from, campaign, send_at, window | mixed | 应用于每条消息的默认值;请参阅“创建消息”。 |
| pace | object | { per_minute } 或 { over_hours }:以固定速率逐步发送批次,或将其均匀分布在 N 小时内。可与 window 结合使用。 |
{
"batch_id": "bat_3Hq9Zr",
"accepted": 2, "rejected": 1,
"total_price": "0.0214", "currency": "USD",
"messages": [
{ "id": "msg_9Kd2fQ", "to": "+14155550142", "status": "queued", "price": "0.0084" },
{ "id": "msg_9Kd2fR", "to": "+447700900123", "status": "queued", "price": "0.0130" },
{ "to": "+99912345", "status": "rejected", "error": { "code": "unsupported_destination" } }
]
}定时参数
单条消息和批量消息有三种发送时间设置方式,并且可以组合使用:将 window 与 pace 结合是营销群发任务的常用设置。
| 参数 | 类型 | 说明 |
|---|---|---|
| send_at | datetime | 采用 UTC 的 ISO 8601 格式。无论当地时间如何,所有收件人都会在该时刻收到消息。 |
| window.local_start | time | 收件人当地时间的 HH:MM。每个时区的时钟到达该时间时,群发任务开始发送。 |
| window.local_end | time | HH:MM。到此时仍未发送的消息会等待次日的时间窗口。 |
| window.quiet_hours | string | default 应用目标地区的静默时段,off 将其禁用,bypass 将流量标记为事务性流量。 |
| pace.per_minute | integer | 该批次每分钟发送的最大消息数。 |
| pace.over_hours | number | 也可以将批次均匀分布在指定小时数内发送。 |
检索消息
GET/v1/messages/{id}
返回消息对象,其中包含当前状态、时间戳、价格;若发送失败并有运营商代码,也会包含该代码。
列出消息
GET/v1/messages
| 参数 | 类型 | 说明 |
|---|---|---|
| status | string | 六种状态之一。 |
| country | string | 小写的 ISO 3166-1 alpha-2 国家代码。 |
| campaign | string | 群发任务标签。 |
| from_date, to_date | datetime | 创建时间范围,包含边界,采用 UTC。 |
| limit | integer | 1 至 200,默认值为 50。 |
| cursor | string | 上一页 next_cursor 中的不透明游标。 |
{ "data": [ { "id": "msg_9Kd2fQ", "status": "delivered", "...": "..." } ],
"next_cursor": "eyJpZCI6Im1zZ185S2QyZlEifQ", "has_more": true }费率
GET/v1/rates和GET/v1/rates/{iso}
公开费率表,与价格页面一致。每一项都包含目标地区、拨号代码、每个分段的价格、发件人 ID 规则和静默时段。可缓存一小时;费率每天最多变更一次。无需密钥。
{ "country": "gb", "name": "United Kingdom", "dial": "44",
"price": "0.0130", "currency": "USD",
"sender": "alphanumeric", "quiet_hours": { "start": "08:00", "end": "21:00" },
"updated_at": "2026-09-21" }余额
GET/v1/balance
{ "balance": "1240.60", "currency": "USD", "pending": "0.00",
"low_threshold": "100.00", "updated_at": "2026-09-21T11:40:00Z" }消息对象
| 参数 | 类型 | 说明 |
|---|---|---|
| id | string | 唯一标识符,以 msg_ 开头。 |
| status | string | queued、sent、delivered、failed、expired、rejected,测试密钥还可返回 test。 |
| to, from, text | string | 发送时的值。from 反映经过任何替换后实际使用的发件人。 |
| segments, encoding | integer, string | 分段数,以及编码 gsm7 或 ucs2。 |
| price, currency | string | 所有分段的总价,以美元计。 |
| country | string | 根据号码解析出的目标地区 ISO 代码。 |
| campaign, reference | string | 您的标签,会原样回显。 |
| created_at, sent_at, delivered_at | datetime | 生命周期时间戳;在到达相应阶段前为 null。 |
| carrier_code, failure_reason | string | 失败时返回运营商原始代码及其通俗易懂的英文说明。 |
状态
- 排队中已接受并计价,正在等待线路时隙。
- 已发送已交给运营商。此时计费。
- 已送达手机已确认接收。最终状态。
- 失败运营商无法送达;已设置
carrier_code。最终状态。 - 已过期有效期已过但仍未送达。最终状态。
- 已拒绝发送前被拒绝。从不计费。最终状态。
错误
{ "error": { "code": "insufficient_balance", "message": "Balance 0.42 USD is below the message price 0.85 USD", "field": null } }| HTTP | 代码 | 含义 |
|---|---|---|
| 400 | invalid_request | JSON 格式错误或缺少必填字段;field 会指出该字段。 |
| 400 | invalid_number | to 不是有效的 E.164 号码。 |
| 400 | invalid_sender | from 不是有效的字母数字发件人,也不是您租用的号码。 |
| 400 | text_too_long | 消息长度超过上限(每条消息最多 6 个分段)。 |
| 401 | unauthorized | 密钥缺失、已撤销、权限范围不正确或受到 IP 限制。 |
| 402 | insufficient_balance | 发送此消息会使余额低于零。 |
| 404 | not_found | 消息、批次或国家/地区未知。 |
| 409 | idempotency_conflict | 重复使用密钥时提供了不同的载荷。 |
| 422 | unsupported_destination | 该号段没有可用线路。 |
| 422 | sender_not_allowed | 不允许使用字母数字发件人,且已禁用替换。 |
| 422 | content_blocked | 文本命中可接受使用政策的屏蔽列表(网络钓鱼模式、冒充他人)。 |
| 429 | rate_limited | 超过速率限制(每个密钥每秒 50 次请求)。请遵守 Retry-After。 |
| 5xx | internal | 服务端错误;未发送任何内容。请使用相同的幂等密钥重试。 |
速率限制
每个密钥每秒 50 次请求,按滑动窗口计算。每个批量请求计为一次请求。响应包含 X-RateLimit-Limit、X-RateLimit-Remaining;收到 429 时还会包含以秒为单位的 Retry-After。
变更日志
- 2026-09
window.quiet_hours接受用于事务性流量的bypass。 - 2026-07 批量响应会列出被拒绝的项目及其错误,不再使整个批次失败。
- 2026-06 Webhook 中会回显
reference字段。 - 2026-04 新增
GET /v1/rates/{iso}。