Guida rapida
- Crea un account e ricarica il tuo saldo nella console.
- Apri Chiavi API e crea una chiave. Le chiavi possono essere create dopo che il primo ricarico è stato accreditato. La chiave viene mostrata una sola volta; conservala come segreto.
- Invia una richiesta POST a
/smscon i numeri e il testo. Leggi l'ID della campagna restituito, poi segui la consegna nella console o tramite webhook.
Autenticazione
Ogni richiesta, tranne l'elenco pubblico delle tariffe, include la tua chiave nell'intestazione Authorization. Le chiavi iniziano con sbk_live_. Una nuova chiave sostituisce immediatamente la vecchia.
Authorization: Bearer sbk_live_YOUR_KEYInvia SMS
POST/sms
Crea una campagna, la addebita dal saldo e accoda ogni messaggio. I numeri vengono puliti e deduplicati; quelli non utilizzabili sono restituiti in dropped con un motivo e non vengono addebitati.
| Campo | Tipo | Descrizione |
|---|---|---|
toobbligatorio | stringa o array | Un numero o un array fino a 10,000 numeri in formato internazionale, ad esempio "+447700900461" |
textobbligatorio | stringa | Testo del messaggio, fino a 10 segmenti. Il testo GSM-7 occupa 160 caratteri per segmento, UCS-2 ne occupa 70 |
schedule_atopzionale | stringa | Ora ISO 8601 da 5 minuti a 60 giorni nel futuro. Ometti per inviare ora |
client_refopzionale | stringa | Il tuo riferimento personale, fino a 64 caratteri a-z A-Z 0-9 _ . : -, restituito nei report e nei webhook |
opt_outopzionale | booleano | true aggiunge la riga di opt-out impostata nelle impostazioni del tuo account |
curl -X POST https://sendbulksms.cc/api/v1/sms \
-H "Authorization: Bearer sbk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["+447700900461", "+5511955550123"],
"text": "Hi, your order 4821 ships today.",
"client_ref": "orders-4821"
}'const res = await fetch("https://sendbulksms.cc/api/v1/sms", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.SENDBULKSMS_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ to: ["+447700900461", "+5511955550123"], text: "Hi, your order 4821 ships today.", client_ref: "orders-4821" }),
});
const campaign = await res.json();
console.log(res.status, campaign.id, campaign.cost);import os, requests
r = requests.post(
"https://sendbulksms.cc/api/v1/sms",
headers={"Authorization": "Bearer " + os.environ["SENDBULKSMS_KEY"]},
json={"to": ["+447700900461", "+5511955550123"], "text": "Hi, your order 4821 ships today.", "client_ref": "orders-4821"},
timeout=30,
)
print(r.status_code, r.json()["id"])<?php
$ch = curl_init("https://sendbulksms.cc/api/v1/sms");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("SENDBULKSMS_KEY"), "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["to" => ["+447700900461", "+5511955550123"], "text" => "Hi, your order 4821 ships today.", "client_ref" => "orders-4821"]),
]);
$campaign = json_decode(curl_exec($ch), true);
echo $campaign["id"];Risposta: la campagna con il suo costo e i primi 1.000 messaggi. Recupera altri con i messaggi della campagna.
HTTP/1.1 201 Created
{
"id": "CP-7KQ2-M9XA-4HTD",
"status": "sending",
"source": "api",
"recipients": 2,
"segments": 1,
"encoding": "GSM-7",
"cost": "0.01078",
"refunded": "0.00000",
"client_ref": "orders-4821",
"scheduled_at": null,
"created_at": "2026-11-07T10:32:04.118Z",
"done_at": null,
"counts": { "queued": 2, "sent": 0, "delivered": 0, "failed": 0, "canceled": 0 },
"dropped": [],
"balance": "248.30422",
"messages": [
{ "id": "SM-2RBW-8MZN-QF6K", "campaign_id": "CP-7KQ2-M9XA-4HTD", "to": "+447700900461", "country": "GB", "parts": 1, "price": "0.00819", "status": "queued", "error": null, "created_at": "2026-11-07T10:32:04.118Z", "sent_at": null, "done_at": null }
]
}Stima un invio
POST/estimate
Prende lo stesso corpo di /sms e restituisce i destinatari, i segmenti, la codifica, il costo totale e il costo per paese senza addebitare o inviare nulla.
{ "recipients": 2, "dropped": [], "segments": 1, "encoding": "GSM-7", "cost": "0.01078", "balance": "248.31500",
"countries": [ { "country": "GB", "recipients": 1, "cost": "0.00819" }, { "country": "BR", "recipients": 1, "cost": "0.00259" } ] }Stato messaggio
GET/sms/{message_id}
Restituisce un messaggio: numero, paese, segmenti, prezzo, stato, l'errore per i messaggi falliti e l'ora di ogni passaggio.
Campagne
GET/campaigns/{campaign_id}
La campagna con il suo costo, i rimborsi e un conteggio dei messaggi per stato.
GET/campaigns/{campaign_id}/messages?offset=0&limit=100
Messaggi nell'ordine in cui sono stati accodati, fino a 1.000 per pagina.
POST/campaigns/{campaign_id}/cancel
Annulla una campagna programmata che non è ancora iniziata e rimborsa l'intero costo al saldo.
Saldo e tariffe
GET/balance
{ "balance": "248.31500", "currency": "USD" }GET/rates
Pubblico, non serve la chiave. Ogni paese con il suo prefisso e prezzo per segmento. /rates/GB restituisce un paese.
{ "updated": "2026-11-07", "rates": [ { "country": "GB", "name": "United Kingdom", "calling_code": "+44", "price": "0.00819", "currency": "USD", "unit": "SMS segment" } ] }Webhook di consegna
Imposta un URL webhook nella console sotto Chiavi API. Quando un messaggio raggiunge uno stato finale (consegnato o fallito) inviamo un report POST. Rispondi con qualsiasi stato 2xx entro 8 secondi; altrimenti la chiamata viene ritentata fino a 8 volte in circa 11 ore. Usa id per ignorare un report già elaborato.
POST https://your-server.example/sms-status
Content-Type: application/json
X-SendBulkSMS-Signature: t=1794047524,v1=5f0c3b9e...
{
"event": "message.status",
"id": "evt_8c1d2e4f6a7b9c0d1e2f",
"created_at": "2026-11-07T10:32:09.004Z",
"data": {
"message_id": "SM-2RBW-8MZN-QF6K",
"campaign_id": "CP-7KQ2-M9XA-4HTD",
"to": "+447700900461",
"country": "GB",
"status": "delivered",
"error": null,
"parts": 1,
"price": "0.00819",
"client_ref": "orders-4821",
"done_at": "2026-11-07T10:32:08.912Z"
}
}Controlla la firma prima di fidarti di un report: HMAC-SHA256 di t + "." + raw body con il tuo segreto webhook, confrontato in tempo costante, e rifiuta tempi più vecchi di 5 minuti.
import { createHmac, timingSafeEqual } from "node:crypto";
// raw = the request body exactly as received (string), header = X-SendBulkSMS-Signature
export function verify(raw, 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 = Buffer.from(createHmac("sha256", secret).update(m[1] + "." + raw).digest("hex"));
const got = Buffer.from(m[2]);
return want.length === got.length && timingSafeEqual(want, got);
}import hmac, hashlib, re, time
def verify(raw: bytes, header: str, secret: str) -> bool:
m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", header or "")
if not m or abs(time.time() - int(m.group(1))) > 300:
return False
want = hmac.new(secret.encode(), m.group(1).encode() + b"." + raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(want, m.group(2))<?php
function verify(string $raw, string $header, string $secret): bool {
if (!preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $header, $m) || abs(time() - (int)$m[1]) > 300) return false;
$want = hash_hmac('sha256', $m[1] . '.' . $raw, $secret);
return hash_equals($want, $m[2]);
}Stati
| Stato | Significato |
|---|---|
queued | Accettato e addebitato, in attesa del proprio turno nella coda |
sent | Consegnato alla rotta, in attesa del report di consegna |
delivered | La rete ha confermato la consegna al dispositivo |
failed | Non consegnato; l'importo viene rimborsato e l'errore indica il motivo |
canceled | Parte di una campagna programmata annullata prima dell'invio; rimborsato |
Errori
Gli errori utilizzano codici di stato HTTP e un corpo JSON: { "error": { "code": "insufficient_balance", "message": "..." } }. Una richiesta che restituisce un errore non addebita mai il saldo.
| HTTP | Codice | Quando |
|---|---|---|
| 400 | bad_requestUn campo è mancante o non valido; il messaggio indica quale | Un campo è mancante o non valido; il messaggio indica quale |
| 400 | no_valid_numbersNessuno dei numeri in "to" può essere utilizzato | Nessuno dei numeri in "to" può essere utilizzato |
| 401 | unauthorizedLa chiave API è mancante o non valida | La chiave API è mancante o non valida |
| 402 | insufficient_balanceIl saldo non copre l'invio; nessun addebito è stato effettuato | Il saldo non copre l'invio; nessun addebito è stato effettuato |
| 404 | not_foundLa campagna, il messaggio o l'endpoint non esistono nel tuo account | La campagna, il messaggio o l'endpoint non esistono nel tuo account |
| 409 | not_scheduledSolo una campagna programmata che non è ancora iniziata può essere annullata | Solo una campagna programmata che non è ancora iniziata può essere annullata |
| 413 | too_many_recipientsPiù di 10,000 numeri in una richiesta | Più di 10,000 numeri in una richiesta |
| 429 | rate_limitedPiù di 300 richieste al minuto per questa chiave | Più di 300 richieste al minuto per questa chiave |
| 500 | server_errorRiprova; nessun addebito viene effettuato per una richiesta fallita | Riprova; nessun addebito viene effettuato per una richiesta fallita |
Limiti
- 300 richieste al minuto per chiave API.
- 10,000 numeri per richiesta di invio; 10 segmenti per messaggio.
- I messaggi vengono inviati a un massimo di 200 al minuto per coda, nell'ordine in cui sono stati accettati.
- Pianificazione da 5 minuti a 60 giorni prima.
- I numeri nella tua lista di non invio vengono saltati e restituiti in dropped con codice suppressed; non vengono addebitati.