Bulk SMS API v1
API ขนาดเล็กสำหรับส่ง SMS แบบกลุ่มจากซอฟต์แวร์ของคุณเอง: JSON เข้าและออก, Bearer keys, ราคาและการตรวจสอบเดียวกับคอนโซล, และ webhooks ที่ลงลายเซ็นสำหรับรายงานการส่ง Base 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 และ webhooks คีย์จะเปิดใช้งานหลังจากเติมเครดิตครั้งแรก ส่งคีย์นี้ในทุกคำขอเป็น Bearer token คีย์จะแสดงเพียงครั้งเดียว คุณสามารถเก็บคีย์ได้ถึง 5 คีย์และยกเลิกคีย์ใดก็ได้ทันที
Authorization: Bearer smsb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxอัตรา
สาธารณะ ไม่ต้องใช้คีย์ ราคาต่อ SMS segment สำหรับทุกปลายทาง หรือสำหรับรหัส 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 จะย้อนกลับไปใช้ค่าเริ่มต้นที่บันทึกไว้ใน Settings |
| opt_out | boolean | เพิ่มข้อความยกเลิกการรับของคุณเป็นบรรทัดสุดท้าย (บันทึกไว้ใน Settings ก่อน) |
{
"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 | ตามที่อยู่ใน Quote | |
| send_at | string | เวลา ISO 8601 เพื่อเริ่มต้น 5 นาทีถึง 90 วันข้างหน้า ปล่อยว่างเพื่อส่งทันที |
| name | string | ป้ายชื่อของคุณสำหรับแคมเปญ สูงสุด 80 ตัวอักษร |
| client_ref | string | การอ้างอิงของคุณเอง สูงสุด 64 ตัวอักษร คืนค่าในทุกข้อความและเว็บฮุค |
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
ทำงานจนถึงเวลาเริ่มต้น ค่าใช้จ่ายทั้งหมดจะคืนสู่เครดิตของคุณในหนึ่งบรรทัดบัญชี และสถานะแคมเปญจะเปลี่ยนเป็นยกเลิก
เว็บฮุค
ตั้งค่า endpoint URL ในคอนโซล เราจะ POST JSON สำหรับ message.delivered, message.failed และ campaign.done รวมถึงทดสอบเมื่อคุณกด Send test event แต่ละเหตุการณ์มีสี่ส่วนหัว และคำตอบใดๆ ที่เป็น 2xx ภายใน 10 วินาทีจะนับว่าได้รับแล้ว
| ฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
| Smsbulk-Event | ส่วนหัว | ชื่อเหตุการณ์ เช่น message.delivered |
| Smsbulk-Delivery | ส่วนหัว | รหัสการจัดส่ง เหมือนกันทุกครั้งที่ลองใหม่ของเหตุการณ์หนึ่ง |
| Smsbulk-Timestamp | ส่วนหัว | วินาที Unix เมื่อการพยายามนี้ถูกเซ็น |
| Smsbulk-Signature | ส่วนหัว | sha256= ตามด้วย base64 HMAC-SHA256 ของ timestamp, ขึ้นบรรทัดใหม่และเนื้อหาดิบ ใช้คีย์ sbw_ secret ของคุณ |
การส่งจะถูกลอง 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 | ไม่พบ endpoint, campaign, message หรือรหัส ISO ในบัญชีนี้ | |
| 409 not_scheduled | มีการขอยกเลิก campaign ที่เริ่มต้นไปแล้ว | |
| 422 text_missing, text_too_long, too_many_segments | ข้อความว่างเปล่า, เกิน 2,000 ตัวอักษร, หรือเกิน 10 segments สำหรับผู้รับ | |
| 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 เป็นจริงแต่ไม่มีข้อความที่บันทึกไว้ใน Settings | |
| 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 | หมายเลข +1 ที่ไม่มีสิบเอ็ดหลัก | |
| not_covered | ปลายทางไม่อยู่ในรายการของเรา | |
| duplicate | หมายเลขเดียวกันปรากฏก่อนหน้านี้ในคำขอ | |
| suppressed | หมายเลขนี้อยู่ในรายการห้ามส่งของคุณ |
สถานะและเหตุผล
| ฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
| queued | สถานะ | กำลังรอออกจากคิวของเรา |
| sent | สถานะ | ส่งให้เครือข่ายแล้ว กำลังรอรายงานการส่ง รายงานส่วนใหญ่มาภายในไม่กี่นาที; บางเครือข่ายใช้เวลาถึง 24 ชั่วโมง |
| delivered | สถานะ | เครือข่ายยืนยันว่าโทรศัพท์ได้รับข้อความแล้ว |
| failed | สถานะ | ไม่สามารถส่งได้ ราคาถูกคืนเข้าสู่เครดิตของคุณ |
| canceled | สถานะ | ไม่ถูกส่งเนื่องจากคุณยกเลิก ได้รับเงินคืนแล้ว |
| ฟิลด์ | ประเภท | ความหมาย |
|---|---|---|
| network_error | เหตุผล | ข้อผิดพลาดของเครือข่าย |
| switched_off | เหตุผล | โทรศัพท์ปิดหรืออยู่นอกพื้นที่ครอบคลุม |
| not_in_service | เหตุผล | หมายเลขไม่ให้บริการ |
| blocked | เหตุผล | ถูกบล็อกโดยเครือข่ายหรือผู้รับ |
| landline | เหตุผล | เบอร์บ้านหรือเบอร์ที่ไม่สามารถรับ SMS ได้ |
| content_filter | เหตุผล | ถูกปฏิเสธโดยตัวกรองเนื้อหาของเครือข่าย |
| expired | เหตุผล | หมดอายุ: ไม่มีรายงานก่อนเวลาที่เครือข่ายกำหนด |
| unknown | เหตุผล | ไม่ทราบการตอบสนองจากเครือข่าย |
| canceled | เหตุผล | ยกเลิกก่อนส่ง |
มีคำถามเกี่ยวกับ API? เปิดตั๋วในคอนโซล หรืออ่าน FAQ