Bulk SMS API v1
Isang maliit na REST API para magpadala ng bulk SMS mula sa iyong software: JSON in at out, Bearer keys, parehong presyo at tseke tulad ng console, at naka-sign na webhooks para sa delivery reports. Base URL https://smsbulk.cc/rest/v1
Pangkalahatang-ideya
Magpadala hanggang 8,000 numero bawat request, bawat isa ay may sariling merge values, at ang API ay nagpe-presyo sa bawat recipient gamit ang kanilang sariling merged text, eksaktong tulad ng console. Humingi muna ng quote gamit ang parehong body para malaman ang gastos; walang ipinapadala o sisingilin sa isang quote. Ang mga oras ay ISO 8601 sa UTC, ang pera ay isang string sa US dollars na may limang decimal.
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;Pagpapatunay
Gumawa ng key sa console sa ilalim ng API at webhooks; ang mga key ay magbubukas pagkatapos ng iyong unang credited top-up. Ipadala ito sa bawat request bilang Bearer token. Ang isang key ay ipinapakita minsan; maaari kang magkaroon ng hanggang 5 key at i-revoke ang alinman sa kanila agad.
Authorization: Bearer smsb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxMga Presyo
Pampubliko, hindi kailangan ng key. Ang presyo bawat SMS segment para sa bawat destinasyon, o para sa isang ISO code.
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"
}Credit
GEThttps://smsbulk.cc/rest/v1/credit
{ "credit": "125.40000", "currency": "USD", "account": "A418820037715" }Quote
Nagpe-presyo ng isang send nang hindi ito ipinapadala. Gumagamit ng parehong body tulad ng Send.
POSThttps://smsbulk.cc/rest/v1/quote
| Field | Uri | Kahulugan |
|---|---|---|
| to | string or string[] | Isang numero o listahan. Ang international form na may + ay pinakaligtas; kailangan ng default_country para sa mga lokal na numero. |
| recipients | object[] | Sa halip na to: isang listahan ng { to, fields } kung saan pinupunan ng fields ang mga merge field tulad ng {{name}}. |
| text | string | Ang mensahe, hanggang 2,000 characters at 10 segments pagkatapos i-merge. |
| default_country | string | ISO code na ginagamit para sa mga numerong walang +, tulad ng GB. Babalik sa default na naka-save sa Settings. |
| opt_out | boolean | Idinadagdag ang iyong naka-save na opt-out wording bilang huling linya (i-save muna ito sa 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": []
}Magpadala
Gumagawa ng kampanya, kinukuha ang kabuuang halaga mula sa iyong credit at inilalagay sa pila ang mga mensahe. Sumasagot ng 201 kasama ang kampanya. Ang mga numero na hindi maipapadala ay nakalista sa left_out at hindi kailanman sisingilin.
POSThttps://smsbulk.cc/rest/v1/send
| Field | Uri | Kahulugan |
|---|---|---|
| to / recipients / text / default_country / opt_out | Tulad sa Quote. | |
| send_at | string | ISO 8601 oras para magsimula, 5 minuto hanggang 90 araw sa hinaharap. Iwanan para magpadala ngayon. |
| name | string | Ang label mo para sa campaign, hanggang 80 characters. |
| client_ref | string | Ang sarili mong reference, hanggang 64 characters, ibabalik sa bawat mensahe at 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": []
}Mga Kampanya
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}
Ang isang kampanya ay may kasamang bilang bawat katayuan: queued, sent, delivered, failed at canceled. Listahan ng mensahe na may after at has_more; hanggang 1,000 bawat pahina.
Mga Mensahe
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"
}Kanselahin ang isang naka-schedule na kampanya
POSThttps://smsbulk.cc/rest/v1/campaigns/{id}/cancel
Gumagana hanggang sa oras ng pagsisimula. Ang buong gastos ay babalik sa iyong credit sa isang ledger line at ang katayuan ng kampanya ay magiging canceled.
Webhooks
Magtakda ng endpoint URL sa console. Nagpo-POST kami ng JSON para sa message.delivered, message.failed at campaign.done, kasama ang test kapag pinindot mo ang Send test event. Bawat event ay may apat na header, at anumang 2xx na sagot sa loob ng 10 segundo ay itinuturing na natanggap.
| Field | Uri | Kahulugan |
|---|---|---|
| Smsbulk-Event | header | Pangalan ng event, tulad ng message.delivered. |
| Smsbulk-Delivery | header | Delivery id, pareho sa bawat retry ng isang event. |
| Smsbulk-Timestamp | header | Unix seconds nang ma-sign ang attempt na ito. |
| Smsbulk-Signature | header | sha256= kasunod ng base64 HMAC-SHA256 ng timestamp, isang newline, at ang raw body, na kinakalibrate gamit ang iyong sbw_ secret. |
Ang delivery ay sinusubukan 8 beses: agad, pagkatapos ng 1 minuto, 5 minuto, 15 minuto, 1 oras, 3 oras, 6 oras at 12 oras. Bawat pagtatangka ay ipinapakita sa console delivery log kasama ang HTTP status nito at ang simula ng iyong sagot.
{
"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"
}
}I-verify ang signature
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", ""))Mga Error at Limitasyon
Gumagamit ng HTTP status at JSON body ang mga error: {"error":{"code":"insufficient_credit","message":"..."}}. Walang bayad kapag nag-fail ang request.
| Field | Uri | Kahulugan |
|---|---|---|
| 400 bad_json | Ang body ay hindi JSON. | |
| 401 unauthorized | Nawawala, maling porma, o nirevoke na key. | |
| 402 insufficient_credit | Mas malaki ang kabuuan kaysa sa iyong credit. Magdagdag ng pondo sa console. | |
| 404 not_found | Walang ganitong endpoint, campaign, mensahe o ISO code sa account na ito. | |
| 409 not_scheduled | Hiniling ang cancel para sa isang campaign na nagsimula na. | |
| 422 text_missing, text_too_long, too_many_segments | Walang laman ang mensahe, lampas sa 2,000 characters, o lampas sa 10 segments para sa isang recipient. | |
| 422 no_recipients, too_many, recipients_missing | Walang maipapadalang numero, higit sa 8,000 numero, o walang to o recipients. | |
| 422 schedule_range, bad_send_at | Ang send_at ay nasa labas ng 5 minuto hanggang 90 araw, o hindi wastong oras. | |
| 422 opt_out_unset | Ang opt_out ay true ngunit walang nakasave na wording sa Settings. | |
| 429 rate_limited | Higit sa 300 requests sa isang minuto para sa key na ito. |
Lahat ng sagot ay may X-RateLimit-Limit, X-RateLimit-Remaining, at X-RateLimit-Reset.
Mga Kodigong Hindi Kasama
Ang mga numerong tinanggal bago mag-presyo ay babalik sa left_out kasama ang posisyon nila sa listahan mo, ang value na ipinadala mo, isang code, at ang simpleng dahilan.
| Field | Uri | Kahulugan |
|---|---|---|
| no_country_code | Walang + at walang default na bansa. | |
| too_short / too_long | Mas mababa sa 8 o higit sa 15 digits. | |
| not_a_number | Mga letra o simbolo na hindi bahagi ng isang numero. | |
| nanp_length | Isang +1 numero na walang labing-isang digits. | |
| not_covered | Isang destinasyon na wala sa aming listahan. | |
| duplicate | Ang parehong numero ay lumabas na mas maaga sa request. | |
| suppressed | Ang numero ay nasa iyong do-not-send list. |
Mga status at dahilan
| Field | Uri | Kahulugan |
|---|---|---|
| queued | status | Naghihintay na umalis sa aming pila. |
| sent | status | Naipasa sa network, naghihintay para sa delivery report. Karamihan sa mga report ay dumarating sa loob ng ilang minuto; ang ilang network ay tumatagal ng hanggang 24 na oras. |
| delivered | status | Kinumpirma ng network na natanggap ito ng telepono. |
| failed | status | Hindi naihatid. Ang presyo ay ibinalik sa iyong credit. |
| canceled | status | Hindi naipadala dahil ikaw ay nag-cancel. Na-refund. |
| Field | Uri | Kahulugan |
|---|---|---|
| network_error | dahilan | Error sa network |
| switched_off | dahilan | Naka-off ang telepono o wala sa coverage |
| not_in_service | dahilan | Numero na hindi na serbisyo |
| blocked | dahilan | Hinarang ng network o ng tatanggap |
| landline | dahilan | Landline o numero na hindi makakatanggap ng SMS |
| content_filter | dahilan | Tinanggihan ng content filter ng network |
| expired | dahilan | Nag-expire: walang report bago mag-expire ang time limit ng network |
| unknown | dahilan | Hindi kilalang response ng network |
| canceled | dahilan | Nakansela bago maipadala |
May tanong tungkol sa API? Magbukas ng ticket sa console, o basahin ang FAQ.