Início rápido
- Crie uma conta e recarregue seu saldo no console.
- Abra Chaves API e crie uma chave. As chaves podem ser criadas após o primeiro crédito de recarga. A chave é mostrada uma vez; armazene-a como um segredo.
- Envie uma requisição POST para
/smscom os números e o texto. Leia o ID da campanha retornado, então acompanhe a entrega no console ou através de webhooks.
Autenticação
Toda requisição, exceto a lista pública de tarifas, carrega sua chave no cabeçalho Authorization. As chaves começam com sbk_live_. Uma nova chave substitui a antiga imediatamente.
Authorization: Bearer sbk_live_YOUR_KEYEnviar SMS
POST/sms
Cria uma campanha, cobra do saldo e coloca cada mensagem na fila. Os números são limpos e deduplicados; aqueles que não podem ser usados são retornados em dropped com um motivo e não são cobrados.
| Campo | Tipo | Descrição |
|---|---|---|
toobrigatório | string ou array | Um número ou uma lista de até 10,000 números no formato internacional, por exemplo "+447700900461" |
textobrigatório | string | Texto da mensagem, até 10 segmentos. Texto GSM-7 cabe 160 caracteres por segmento, UCS-2 cabe 70 |
schedule_atopcional | string | Hora ISO 8601 de 5 minutos a 60 dias à frente. Omita para enviar agora |
client_refopcional | string | Sua própria referência, até 64 caracteres a-z A-Z 0-9 _ . : -, retornada em relatórios e webhooks |
opt_outopcional | booleano | true adiciona a linha de opt-out definida nas configurações da sua conta |
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"];Resposta: a campanha com seu custo e as primeiras 1.000 mensagens. Obtenha mais com as mensagens da campanha.
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 }
]
}Estimar um envio
POST/estimate
Recebe o mesmo corpo que /sms e retorna os destinatários, segmentos, codificação, custo total e o custo por país sem cobrar ou enviar nada.
{ "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" } ] }Status da mensagem
GET/sms/{message_id}
Retorna uma mensagem: número, país, segmentos, preço, status, o erro para mensagens falhas e o tempo de cada etapa.
Campanhas
GET/campaigns/{campaign_id}
A campanha com seu custo, reembolsos e uma contagem de mensagens por status.
GET/campaigns/{campaign_id}/messages?offset=0&limit=100
Mensagens na ordem em que foram enfileiradas, até 1.000 por página.
POST/campaigns/{campaign_id}/cancel
Cancela uma campanha agendada que não começou e reembolsa seu custo total para o saldo.
Saldo e tarifas
GET/balance
{ "balance": "248.31500", "currency": "USD" }GET/rates
Público, não precisa de chave. Cada país com seu código de chamada e preço por segmento. /rates/GB retorna um país.
{ "updated": "2026-11-07", "rates": [ { "country": "GB", "name": "United Kingdom", "calling_code": "+44", "price": "0.00819", "currency": "USD", "unit": "SMS segment" } ] }Webhooks de entrega
Defina uma URL de webhook no console em Chaves API. Quando uma mensagem atinge um status final (entregue ou falha), enviamos um relatório POST para ela. Responda com qualquer status 2xx em até 8 segundos; caso contrário, a chamada será repetida até 8 vezes em cerca de 11 horas. Use id para ignorar um relatório que você já processou.
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"
}
}Verifique a assinatura antes de confiar em um relatório: HMAC-SHA256 de t + "." + raw body com seu segredo de webhook, comparado em tempo constante, e rejeite tempos mais antigos que 5 minutos.
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]);
}Status
| Status | Significado |
|---|---|
queued | Aceito e cobrado, aguardando sua vez na fila |
sent | Encaminhado para a rota, aguardando relatório de entrega |
delivered | A rede confirmou a entrega no aparelho |
failed | Não entregue; o valor é reembolsado e o erro contém o motivo |
canceled | Parte de uma campanha agendada cancelada antes de iniciar; reembolsado |
Erros
Erros usam códigos de status HTTP e um corpo JSON: { "error": { "code": "insufficient_balance", "message": "..." } }. Uma solicitação que retorna um erro nunca cobra o saldo.
| HTTP | Código | Quando |
|---|---|---|
| 400 | bad_requestUm campo está faltando ou é inválido; a mensagem explica qual | Um campo está faltando ou é inválido; a mensagem explica qual |
| 400 | no_valid_numbersNenhum dos números em "to" pôde ser usado | Nenhum dos números em "to" pôde ser usado |
| 401 | unauthorizedA chave API está faltando ou é inválida | A chave API está faltando ou é inválida |
| 402 | insufficient_balanceO saldo não cobre o envio; nada foi cobrado | O saldo não cobre o envio; nada foi cobrado |
| 404 | not_foundA campanha, mensagem ou endpoint não existe em sua conta | A campanha, mensagem ou endpoint não existe em sua conta |
| 409 | not_scheduledApenas uma campanha agendada que não começou pode ser cancelada | Apenas uma campanha agendada que não começou pode ser cancelada |
| 413 | too_many_recipientsMais de 10,000 números em uma solicitação | Mais de 10,000 números em uma solicitação |
| 429 | rate_limitedMais de 300 solicitações em um minuto para esta chave | Mais de 300 solicitações em um minuto para esta chave |
| 500 | server_errorTente novamente; nenhuma cobrança é feita por uma solicitação que falha | Tente novamente; nenhuma cobrança é feita por uma solicitação que falha |
Limites
- 300 solicitações por minuto por chave API.
- 10,000 números por solicitação de envio; 10 segmentos por mensagem.
- As mensagens são enviadas a até 200 por minuto por fila, na ordem em que foram aceitas.
- Agendamento de 5 minutos a 60 dias antes.
- Números em sua lista de não-envio são ignorados e retornados em dropped com código suppressed; eles não são cobrados.