API SMS hàng loạt v1
Một API REST nhỏ để gửi SMS hàng loạt từ phần mềm của bạn: JSON vào và ra, khóa Bearer, cùng giá và kiểm tra như trên bảng điều khiển, và webhooks được ký cho báo cáo giao hàng. URL cơ sở https://smsbulk.cc/rest/v1
Tổng quan
Gửi đến tối đa 8,000 số mỗi yêu cầu, mỗi số có giá trị hợp nhất riêng, và API định giá mỗi người nhận với văn bản hợp nhất của họ, giống như trên bảng điều khiển. Yêu cầu báo giá trước với cùng nội dung để biết chi phí; không có gì được gửi hoặc tính phí bởi báo giá. Thời gian là ISO 8601 trong UTC, tiền là chuỗi đô la Mỹ với năm chữ số thập phân.
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;Xác thực
Tạo một khóa trong bảng điều khiển dưới API và webhooks; khóa mở sau khi bạn nạp tiền lần đầu tiên. Gửi nó trên mỗi yêu cầu như một token Bearer. Một khóa được hiển thị một lần; bạn có thể giữ tối đa 5 khóa và thu hồi bất kỳ khóa nào ngay lập tức.
Authorization: Bearer smsb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGiá
Công khai, không cần khóa. Giá mỗi phân đoạn SMS cho mọi điểm đến, hoặc cho một mã 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"
}Số dư
GEThttps://smsbulk.cc/rest/v1/credit
{ "credit": "125.40000", "currency": "USD", "account": "A418820037715" }Báo giá
Định giá một lần gửi mà không gửi nó. Lấy cùng nội dung như Send.
POSThttps://smsbulk.cc/rest/v1/quote
| Trường | Loại | Ý nghĩa |
|---|---|---|
| to | string or string[] | Một số hoặc danh sách. Dạng quốc tế với + là an toàn nhất; số địa phương cần default_country. |
| recipients | object[] | Thay vì to: một danh sách { to, fields } trong đó fields điền vào các trường hợp nhất như {{name}}. |
| text | string | Tin nhắn, tối đa 2,000 ký tự và 10 phân đoạn sau khi hợp nhất. |
| default_country | string | Mã ISO được sử dụng cho các số không có +, ví dụ GB. Quay về mặc định đã lưu trong Cài đặt. |
| opt_out | boolean | Thêm dòng từ chối nhận tin đã lưu của bạn làm dòng cuối (lưu trước trong Cài đặt). |
{
"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": []
}Gửi
Tạo một chiến dịch, lấy tổng số từ số dư của bạn và xếp hàng đợi các tin nhắn. Trả lời 201 với chiến dịch. Các số không thể gửi được liệt kê trong left_out và không bao giờ bị tính phí.
POSThttps://smsbulk.cc/rest/v1/send
| Trường | Loại | Ý nghĩa |
|---|---|---|
| to / recipients / text / default_country / opt_out | Như trong Quote. | |
| send_at | string | Thời gian ISO 8601 để bắt đầu, từ 5 phút đến 90 ngày sau. Bỏ qua để gửi ngay. |
| name | string | Nhãn của bạn cho chiến dịch, tối đa 80 ký tự. |
| client_ref | string | Tham chiếu của bạn, tối đa 64 ký tự, được trả về trên mọi tin nhắn và 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": []
}Chiến dịch
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}
Một chiến dịch duy nhất bao gồm số lượng theo trạng thái: đang chờ, đã gửi, đã giao, thất bại và đã hủy. Trang danh sách tin nhắn với after và has_more; tối đa 1,000 mỗi trang.
Tin nhắn
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"
}Hủy một chiến dịch đã lên lịch
POSThttps://smsbulk.cc/rest/v1/campaigns/{id}/cancel
Hoạt động cho đến thời gian bắt đầu. Toàn bộ chi phí trả lại vào số dư của bạn trong một dòng sổ cái và trạng thái chiến dịch trở thành đã hủy.
Webhooks
Đặt một URL endpoint trong bảng điều khiển. Chúng tôi POST JSON cho message.delivered, message.failed và campaign.done, cộng với kiểm tra khi bạn nhấn Gửi sự kiện kiểm tra. Mỗi sự kiện mang bốn tiêu đề, và bất kỳ câu trả lời 2xx nào trong vòng 10 giây được tính là đã nhận.
| Trường | Loại | Ý nghĩa |
|---|---|---|
| Smsbulk-Event | tiêu đề | Tên sự kiện, ví dụ message.delivered. |
| Smsbulk-Delivery | tiêu đề | ID giao hàng, giống nhau trên mỗi lần thử lại của một sự kiện. |
| Smsbulk-Timestamp | tiêu đề | Giây Unix khi lần thử này được ký. |
| Smsbulk-Signature | tiêu đề | sha256= theo sau là base64 HMAC-SHA256 của timestamp, dòng mới và nội dung thô, được mã hóa bằng khóa bí mật sbw_ của bạn. |
Giao hàng được thử 8 lần: ngay lập tức, sau đó sau 1 phút, 5 phút, 15 phút, 1 giờ, 3 giờ, 6 giờ và 12 giờ. Mỗi lần thử hiển thị trong nhật ký giao hàng của bảng điều khiển với trạng thái HTTP và phần đầu của câu trả lời của bạn.
{
"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"
}
}Xác minh chữ ký
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", ""))Lỗi và giới hạn
Lỗi sử dụng mã trạng thái HTTP và nội dung JSON: {"error":{"code":"insufficient_credit","message":"..."}}. Không tính phí khi yêu cầu thất bại.
| Trường | Loại | Ý nghĩa |
|---|---|---|
| 400 bad_json | Nội dung không phải là JSON. | |
| 401 unauthorized | Thiếu, sai định dạng hoặc khóa đã bị thu hồi. | |
| 402 insufficient_credit | Tổng số tiền vượt quá số dư của bạn. Hãy nạp thêm tiền trong bảng điều khiển. | |
| 404 not_found | Không có điểm cuối, chiến dịch, tin nhắn hoặc mã ISO nào như vậy trên tài khoản này. | |
| 409 not_scheduled | Yêu cầu hủy cho một chiến dịch đã bắt đầu. | |
| 422 text_missing, text_too_long, too_many_segments | Tin nhắn trống, vượt quá 2,000 ký tự, hoặc vượt quá 10 phân đoạn cho một người nhận. | |
| 422 no_recipients, too_many, recipients_missing | Không có số điện thoại có thể gửi, nhiều hơn 8,000 số, hoặc không có cả to và recipients. | |
| 422 schedule_range, bad_send_at | send_at nằm ngoài khoảng 5 phút đến 90 ngày, hoặc không phải là thời gian hợp lệ. | |
| 422 opt_out_unset | opt_out là true nhưng không có câu từ nào được lưu trong Cài đặt. | |
| 429 rate_limited | Hơn 300 yêu cầu trong một phút cho khóa này. |
Mỗi phản hồi đều chứa X-RateLimit-Limit, X-RateLimit-Remaining và X-RateLimit-Reset.
Mã bị bỏ qua
Các số bị loại bỏ trước khi tính giá sẽ được trả về trong left_out cùng với vị trí trong danh sách, giá trị bạn gửi, mã và lý do cụ thể.
| Trường | Loại | Ý nghĩa |
|---|---|---|
| no_country_code | Không có + và không có quốc gia mặc định. | |
| too_short / too_long | Dưới 8 hoặc trên 15 chữ số. | |
| not_a_number | Chữ cái hoặc ký hiệu không phải là một phần của số. | |
| nanp_length | Một số +1 không có mười một chữ số. | |
| not_covered | Một điểm đến nằm ngoài danh sách của chúng tôi. | |
| duplicate | Số điện thoại này đã xuất hiện trước đó trong yêu cầu. | |
| suppressed | Số điện thoại này nằm trong danh sách không gửi của bạn. |
Trạng thái và lý do
| Trường | Loại | Ý nghĩa |
|---|---|---|
| queued | trạng thái | Đang chờ rời khỏi hàng đợi của chúng tôi. |
| sent | trạng thái | Đã chuyển cho mạng, đang chờ báo cáo giao hàng. Hầu hết các báo cáo đến trong vòng vài phút; một số mạng mất đến 24 giờ. |
| delivered | trạng thái | Mạng đã xác nhận điện thoại đã nhận được tin nhắn. |
| failed | trạng thái | Không giao được. Giá đã được hoàn lại vào số dư của bạn. |
| canceled | trạng thái | Không gửi vì bạn đã hủy. Đã hoàn tiền. |
| Trường | Loại | Ý nghĩa |
|---|---|---|
| network_error | lý do | Lỗi mạng |
| switched_off | lý do | Điện thoại tắt hoặc ngoài vùng phủ sóng |
| not_in_service | lý do | Số điện thoại không còn hoạt động |
| blocked | lý do | Bị chặn bởi mạng hoặc người nhận |
| landline | lý do | Số cố định hoặc số không thể nhận SMS |
| content_filter | lý do | Bị từ chối bởi bộ lọc nội dung của mạng |
| expired | lý do | Hết hạn: không có báo cáo trước thời hạn của mạng |
| unknown | lý do | Phản hồi mạng không xác định |
| canceled | lý do | Đã hủy trước khi gửi |
Có thắc mắc về API? Mở một ticket trong console hoặc đọc FAQ.