大量 SMS API v1
一個簡潔的REST API,讓您能從自己的軟體發送大量簡訊:使用JSON格式輸入輸出、Bearer金鑰,價格與檢查機制與網頁控制台相同,並提供簽章驗證的webhook接收送達報告。 基礎URL https://smsbulk.cc/rest/v1
概述
每個請求最多可發送至8,000個號碼,每個號碼可帶入各自的合併欄位值。API會像控制台一樣,根據每位收件者合併後的文字內容精確計算費用。發送前可先使用相同內容取得報價,報價不會實際發送或收費。時間格式為ISO 8601標準(UTC時區),金額以五位小數的美金字串表示。
curl https://smsbulk.cc/rest/v1/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": [
{ "to": "+14155550142", "fields": { "name": "Maria" } },
{ "to": "+447400123456", "fields": { "name": "Tom" } }
],
"text": "Hi {{name}}, your order ships today.",
"client_ref": "order-batch-17"
}'const res = await fetch("https://smsbulk.cc/rest/v1/send", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
recipients: [
{ to: "+14155550142", fields: { name: "Maria" } },
{ to: "+447400123456", fields: { name: "Tom" } },
],
text: "Hi {{name}}, your order ships today.",
client_ref: "order-batch-17",
}),
});
const data = await res.json();
console.log(res.status, data.campaign?.id, data.campaign?.cost);import requests
res = requests.post(
"https://smsbulk.cc/rest/v1/send",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"recipients": [
{"to": "+14155550142", "fields": {"name": "Maria"}},
{"to": "+447400123456", "fields": {"name": "Tom"}},
],
"text": "Hi {{name}}, your order ships today.",
"client_ref": "order-batch-17",
},
timeout=30,
)
data = res.json()
print(res.status_code, data.get("campaign", {}).get("id"))<?php
$body = json_encode([
"recipients" => [
["to" => "+14155550142", "fields" => ["name" => "Maria"]],
["to" => "+447400123456", "fields" => ["name" => "Tom"]],
],
"text" => "Hi {{name}}, your order ships today.",
"client_ref" => "order-batch-17",
]);
$ch = curl_init("https://smsbulk.cc/rest/v1/send");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$data = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $data["campaign"]["id"] ?? "", PHP_EOL;認證
在控制台的API與webhook區建立金鑰,首次儲值後金鑰才會生效。每次請求需以Bearer token形式攜帶金鑰。金鑰僅顯示一次,您最多可保留5組金鑰並隨時撤銷任一組。
Authorization: Bearer smsb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx費率
公開API無需金鑰。查詢所有目的地或指定ISO代碼的每則簡訊分段價格。
GEThttps://smsbulk.cc/rest/v1/rates
GEThttps://smsbulk.cc/rest/v1/rates/{ISO}
{
"country": "US",
"name": "United States",
"dial_code": "+1",
"rate": "0.00485",
"currency": "USD",
"per": "message segment"
}餘額
GEThttps://smsbulk.cc/rest/v1/credit
{ "credit": "125.40000", "currency": "USD", "account": "A418820037715" }報價
模擬發送並計算費用,參數格式與Send相同但不會實際發送。
POSThttps://smsbulk.cc/rest/v1/quote
| 欄位 | 類型 | 意義 |
|---|---|---|
| to | string or string[] | 可為單一號碼或清單。使用國際格式(含+號)最安全;本地號碼需指定 default_country。 |
| recipients | object[] | 可替代 to:使用 { to, fields } 清單,其中 fields 用於填充合併欄位如 {{name}}。 |
| text | string | 訊息內容,合併後最多 2,000 個字元與 10 個分段。 |
| default_country | string | 用於未帶+號的號碼之ISO代碼(如GB)。若未指定則退回設定中儲存的預設值。 |
| opt_out | boolean | 將您儲存的退訂聲明添加為最後一行(需先在設定中儲存)。 |
{
"recipients": 2,
"messages": 2,
"segments": { "min": 1, "max": 1 },
"encoding": "GSM-7",
"total": "0.01240",
"currency": "USD",
"credit": "125.40000",
"credit_after": "125.38760",
"countries": [
{ "country": "US", "recipients": 1, "rate": "0.00485", "segments": 1, "subtotal": "0.00485" },
{ "country": "GB", "recipients": 1, "rate": "0.00755", "segments": 1, "subtotal": "0.00755" }
],
"left_out": []
}發送
建立發送任務,從餘額扣除總費用並將訊息排入佇列。回傳201狀態碼及任務資料。無法發送的號碼會列於left_out欄位且不計費。
POSThttps://smsbulk.cc/rest/v1/send
| 欄位 | 類型 | 意義 |
|---|---|---|
| to / recipients / text / default_country / opt_out | 同報價功能。 | |
| send_at | string | 以ISO 8601格式指定開始時間,可設定5分鐘後至90天內。留空則立即發送。 |
| name | string | 活動標籤,最多80個字元。 |
| client_ref | string | 自訂參考代號(最多64字元),將隨每則訊息及webhook回傳。 |
HTTP/1.1 201 Created
{
"campaign": {
"id": "C507318264409",
"name": null,
"status": "sending",
"source": "api",
"test": false,
"recipients": 2,
"cost": "0.01240",
"refunded": "0.00000",
"net_cost": "0.01240",
"currency": "USD",
"send_at": null,
"created_at": "2026-11-07T14:02:11.000Z",
"started_at": "2026-11-07T14:02:11.000Z",
"finished_at": null,
"client_ref": "order-batch-17"
},
"messages": 2,
"left_out": []
}發送任務
GEThttps://smsbulk.cc/rest/v1/campaigns?limit=25&before={id}
GEThttps://smsbulk.cc/rest/v1/campaigns/{id}
GEThttps://smsbulk.cc/rest/v1/campaigns/{id}/messages?status=failed&limit=100&after={message id}
單一發送任務包含各狀態計數:排隊中、已發送、已送達、失敗與已取消。訊息列表支援分頁查詢(after與has_more參數),每頁最多1,000筆。
訊息
GEThttps://smsbulk.cc/rest/v1/messages/{id}
GEThttps://smsbulk.cc/rest/v1/messages?to=%2B14155550142&status=delivered&client_ref=order-batch-17&limit=50
{
"id": "M220947781305",
"campaign": "C507318264409",
"to": "+14155550142",
"country": "US",
"segments": 1,
"encoding": "GSM-7",
"price": "0.00485",
"status": "delivered",
"reason": null,
"reason_text": null,
"refunded": false,
"client_ref": "order-batch-17",
"created_at": "2026-11-07T14:02:11.000Z",
"sent_at": "2026-11-07T14:02:12.000Z",
"finished_at": "2026-11-07T14:02:16.000Z"
}取消預約發送
POSThttps://smsbulk.cc/rest/v1/campaigns/{id}/cancel
在預定發送時間前可取消,全額費用將以單一紀錄返還至您的餘額,任務狀態變更為已取消。
Webhook
在控制台設定接收URL端點。我們會POST JSON格式的message.delivered(送達)、message.failed(失敗)與campaign.done(任務完成)事件,點擊「發送測試事件」時也會觸發測試。每個事件帶有四個標頭,10秒內回傳2xx狀態碼即視為接收成功。
| 欄位 | 類型 | 意義 |
|---|---|---|
| Smsbulk-Event | 標頭 | 事件名稱,例如 message.delivered。 |
| Smsbulk-Delivery | 標頭 | 遞送ID,同一事件每次重試時保持相同。 |
| Smsbulk-Timestamp | 標頭 | 此嘗試簽署時的Unix時間戳(秒)。 |
| Smsbulk-Signature | 標頭 | sha256= 後接以您 sbw_ 密鑰對時間戳、換行符及原始主體進行HMAC-SHA256運算後的base64值。 |
系統會嘗試發送8次:立即發送後,分別於1分鐘、5分鐘、15分鐘、1小時、3小時、6小時和12小時後重試。每次嘗試皆會記錄於控制台的傳送日誌,包含HTTP狀態碼與您的回傳內容開頭。
{
"id": "dlv_3f9a1c07b2e44d58a6c1",
"event": "message.failed",
"created": "2026-11-07T14:02:16.000Z",
"data": {
"message": "M220947781305",
"campaign": "C507318264409",
"to": "+447400123456",
"country": "GB",
"status": "failed",
"reason": "not_in_service",
"segments": 1,
"price": "0.00755",
"client_ref": "order-batch-17"
}
}驗證簽名
import crypto from "node:crypto";
// raw = the request body exactly as received (string), secret = your sbw_ signing secret
export function verify(raw, headers, secret) {
const ts = headers["smsbulk-timestamp"];
const want = "sha256=" + crypto.createHmac("sha256", secret).update(ts + "\n" + raw).digest("base64");
const got = headers["smsbulk-signature"] || "";
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
return fresh && got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want));
}import base64, hashlib, hmac, time
def verify(raw: bytes, headers, secret: str) -> bool:
ts = headers.get("Smsbulk-Timestamp", "")
mac = hmac.new(secret.encode(), ts.encode() + b"\n" + raw, hashlib.sha256).digest()
want = "sha256=" + base64.b64encode(mac).decode()
fresh = abs(time.time() - int(ts or 0)) < 300
return fresh and hmac.compare_digest(want, headers.get("Smsbulk-Signature", ""))錯誤與限制
錯誤會使用 HTTP 狀態碼和 JSON 回應主體:{"error":{"code":"insufficient_credit","message":"..."}}。請求失敗時不會收取任何費用。
| 欄位 | 類型 | 意義 |
|---|---|---|
| 400 bad_json | 主體非JSON格式。 | |
| 401 unauthorized | 密鑰遺失、格式錯誤或已撤銷。 | |
| 402 insufficient_credit | 總金額超過您的餘額。請在控制台中充值。 | |
| 404 not_found | 此帳戶中沒有此端點、活動、訊息或ISO代碼。 | |
| 409 not_scheduled | 已開始的活動被要求取消。 | |
| 422 text_missing, text_too_long, too_many_segments | 訊息為空、超過2,000個字符,或對一個收件人超過10個分段。 | |
| 422 no_recipients, too_many, recipients_missing | 無可發送號碼、超過8,000個號碼,或既無to也無recipients。 | |
| 422 schedule_range, bad_send_at | send_at在5分鐘到90天之外,或不是有效時間。 | |
| 422 opt_out_unset | opt_out為true但設置中未保存相關文字。 | |
| 429 rate_limited | 此密鑰每分鐘請求超過300次。 |
每個回應都會包含 X-RateLimit-Limit、X-RateLimit-Remaining 和 X-RateLimit-Reset。
排除的號碼
在計價前被移除的號碼會以 left_out 形式回傳,包含其在清單中的位置、您傳送的值、代碼及簡明原因。
| 欄位 | 類型 | 意義 |
|---|---|---|
| no_country_code | 無+且無默認國家。 | |
| too_short / too_long | 少於8位或多於15位。 | |
| not_a_number | 不屬於數字的字母或符號。 | |
| nanp_length | 不足 11 位數的 +1 號碼。 | |
| not_covered | 目的地不在我們的列表中。 | |
| duplicate | 請求中之前出現過相同的號碼。 | |
| suppressed | 此號碼在您的禁止發送列表中。 |
狀態與原因
| 欄位 | 類型 | 意義 |
|---|---|---|
| queued | 狀態 | 等待離開我們的隊列。 |
| sent | 狀態 | 已交給網絡,等待送達報告。大多數報告在幾分鐘內到達;某些網絡可能需要長達24小時。 |
| delivered | 狀態 | 網絡確認手機已收到。 |
| failed | 狀態 | 未送達。價格已退還至您的餘額。 |
| canceled | 狀態 | 因您取消而未發送。已退款。 |
| 欄位 | 類型 | 意義 |
|---|---|---|
| network_error | 原因 | 網絡錯誤 |
| switched_off | 原因 | 手機關機或不在服務範圍內 |
| not_in_service | 原因 | 號碼已停用 |
| blocked | 原因 | 被電信商或收件人封鎖 |
| landline | 原因 | 固網電話或無法接收簡訊的號碼 |
| content_filter | 原因 | 被電信商內容過濾系統拒絕 |
| expired | 原因 | 已過期:在電信商時限內未收到回報 |
| unknown | 原因 | 未知的電信商回應 |
| canceled | 原因 | 發送前已取消 |
對 API 有疑問?請在控制台提交問題單,或閱讀常見問答。