群发短信 API 参考

BulkSMSOnline REST API 可从您的软件向 227 个国家发送短信,价格、分段规则、定时发送和退款政策与控制台一致。请求和响应通过 HTTPS 以 JSON 格式传输,所有最终送达状态均可通过签名 Webhook 推送到您的服务器。

基础 URLhttps://bulksmsonline.cc/api/v1

概述

当消息由您自己的系统触发时使用API:来自商店的订单更新、登录表单的验证码、预约工具的提醒。向POST /v1/messages发送最多10,000个号码的请求会创建一个活动,从余额中扣费并将每条消息加入队列。通过API创建的活动也会在控制台中显示其送达报告。

  • 所有金额均为美元,以五位小数字符串表示,例如"0.00534"。
  • 电话号码为国际格式,可带或不带开头的+号;空格和破折号会被移除。
  • 时间采用ISO 8601格式的UTC时间,例如2026-12-01T09:00:00Z。
  • 消息和活动ID的格式类似MSG-XXXX-XXXX-XXXX和CMP-XXXX-XXXX-XXXX。

身份验证

在控制台的API下创建API密钥(首次充值后密钥功能开启)。在每个请求的Authorization头中发送该密钥。密钥格式为bso_api_后接36个字符;仅显示一次,请将其作为机密存储在服务器上。创建新密钥会立即替换旧密钥。

Authorization: Bearer bso_api_0123456789abcdef0123456789abcdef0123
Content-Type: application/json

限制

请求每个密钥每分钟300次;超出将返回429 rate_limited
每请求号码数10,000
消息文本按书写字符计2,000个字符,最多10个分段
定时发送send_at从5分钟后到60天内
发送速率每个队列每分钟400条消息
Webhook重试9次尝试,约22小时内

发送消息

POST/v1/messages

向一个或多个号码发送一条短信。请求被接受时,费用会从余额中扣除;每条无法送达的消息稍后会退款。响应状态为201,并返回包含每条消息的群发活动信息。

字段描述
to必填一个号码或最多包含10,000个号码的数组,需为国际格式。重复号码仅发送一次;无法识别的号码会列在跳过列表中。
text必填消息内容,最多2,000个字符和10条短信分段。GSM-7编码每条短信最多160个字符,Unicode编码为70个字符。
send_at可选使用ISO 8601格式设置发送时间,可预约5分钟后至60天内的发送。留空表示立即发送。
opt_out可选设为true时,会在消息末尾新起一行添加您在控制台设置的退订提示。
client_ref可选您自定义的参考标识,最多64个字母、数字和_ . : -字符。会在报告和webhook中返回。
curl -X POST https://bulksmsonline.cc/api/v1/messages \
  -H "Authorization: Bearer $BSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":["+447700900412","+33612345678"],"text":"Order 4521 has shipped. Track it at shop.example/t/4521","client_ref":"order-4521"}'
HTTP/1.1 201 Created
{
  "id": "CMP-7KQ2-M9XA-4HTD",
  "status": "sending",
  "channel": "api",
  "recipients": 2,
  "segments": 1,
  "encoding": "GSM-7",
  "cost": "0.02158",
  "refunded": "0.00000",
  "client_ref": "order-4521",
  "send_at": null,
  "counts": { "queued": 2, "submitted": 0, "delivered": 0, "failed": 0, "canceled": 0 },
  "skipped": [],
  "balance": "118.43120",
  "messages": [
    { "id": "MSG-3RTW-81KD-ZQ0P", "to": "+447700900412", "country": "GB", "segments": 1, "price": "0.00834", "status": "queued" },
    { "id": "MSG-5HNA-4C2E-7YVB", "to": "+33612345678", "country": "FR", "segments": 1, "price": "0.01324", "status": "queued" }
  ]
}

发送报价

POST/v1/quote

接收与/v1/messages相同的请求体,返回精确的费用、分段和编码信息,但不实际发送或扣费。可用于在用户确认前展示价格。

{
  "recipients": 2,
  "skipped": [],
  "segments": 1,
  "encoding": "GSM-7",
  "cost": "0.02158",
  "balance": "118.45278",
  "countries": [ { "country": "GB", "recipients": 1, "cost": "0.00834" }, { "country": "FR", "recipients": 1, "cost": "0.01324" } ]
}

余额

GET/v1/balance

{ "balance": "118.43120", "currency": "USD" }

费率

GET/v1/rates

GET/v1/rates/{country}

获取所有目的地或指定两字母ISO国家代码的每条短信分段价格。这两个端点为公开接口,无需密钥。

{ "country": "DE", "name": "Germany", "calling_code": "+49", "price": "0.01544", "currency": "USD", "per": "segment" }

消息状态

GET/v1/messages/{id}

{
  "id": "MSG-3RTW-81KD-ZQ0P",
  "campaign_id": "CMP-7KQ2-M9XA-4HTD",
  "to": "+447700900412",
  "country": "GB",
  "segments": 1,
  "price": "0.00834",
  "status": "delivered",
  "error": null,
  "created_at": "2026-11-07T09:00:02Z",
  "submitted_at": "2026-11-07T09:00:03Z",
  "final_at": "2026-11-07T09:00:08Z"
}

活动

GET/v1/campaigns/{id}

GET/v1/campaigns/{id}/messages?offset=0&limit=100

第一个接口返回群发活动及各状态计数;第二个接口分页查询活动消息,每页最多1000条。从控制台发送的活动也可通过页面显示的ID以相同方式查询。

取消预约发送

POST/v1/campaigns/{id}/cancel

取消通过send_at创建且尚未开始的群发活动。所有消息将被取消,全额费用退回余额。已开始发送的活动将返回409 not_scheduled错误。

送达 Webhook

在控制台API设置中添加HTTPS URL,以POST请求接收每条最终状态(送达或失败)。需在8秒内返回任意2xx状态码。其他响应和超时会在20秒、1、5、15分钟后重试,之后在1、3、6、12小时重试,共9次尝试。同一事件可能重复到达,请使用event_id确保只处理一次。

POST https://your-server.example/sms-status
Content-Type: application/json
X-BulkSMSOnline-Signature: t=1794042008,v1=5f0c2a...e91b

{
  "type": "message.status",
  "event_id": "ev_8f1c22d0a4b97e63c1f0",
  "sent_at": "2026-11-07T09:00:09Z",
  "data": {
    "message_id": "MSG-3RTW-81KD-ZQ0P",
    "campaign_id": "CMP-7KQ2-M9XA-4HTD",
    "to": "+447700900412",
    "country": "GB",
    "status": "delivered",
    "error": null,
    "segments": 1,
    "price": "0.00834",
    "currency": "USD",
    "client_ref": "order-4521",
    "final_at": "2026-11-07T09:00:08Z"
  }
}

验证签名

请求头包含Unix时间戳t和签名v1(使用您的webhook密钥对字符串"t.raw_body"进行HMAC-SHA256计算,结果为十六进制)。需在解析JSON前对原始正文计算签名,使用恒定时间比较,并拒绝超过五分钟的事件。

import crypto from "node:crypto";

export function verify(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const want = crypto.createHmac("sha256", secret).update(m[1] + "." + rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(m[2]));
}

状态

queued已接受,等待出队列
submitted已移交至目标网络
delivered已由终端设备确认(最终状态)
failed无法送达;费用已退款(最终状态)
canceled预约发送活动在发送前被取消(最终状态)

群发活动状态为scheduled、sending、complete或canceled。

错误

错误响应包含HTTP状态码和JSON正文:{ "error": { "code": "insufficient_balance", "message": "..." } }。请求返回错误时不会产生任何费用。

HTTP代码含义
400invalid_request字段缺失或无效;错误消息会指明具体字段。
400invalid_json请求体不是JSON格式。
400no_valid_numbers无法读取任何号码。
401unauthorized密钥缺失、格式错误或已被替换。
402insufficient_balance余额不足,请充值后重新发送。
404not_found未知ID或端点。
409not_scheduled仅未开始的预约发送活动可被取消。
413too_many_recipients单次请求超过10,000个号码。
429rate_limited一分钟内请求超过300次。
500server_error请重试请求,未产生任何费用。

在控制台中获取API密钥

创建账户并完成首次充值,在API选项下生成密钥。一分钟后即可发送首次请求。

创建账户
批量短信API参考:REST端点与Webhooks | BulkSMSOnline