Toplu SMS API v1
Kendi yazılımınızdan toplu SMS göndermek için küçük bir REST API: JSON giriş ve çıkış, Bearer anahtarları, konsol ile aynı fiyatlar ve kontroller, ve teslimat raporları için imzalı webhooklar. Temel URL https://smsbulk.cc/rest/v1
Genel Bakış
Her istekte en fazla 8,000 numaraya gönderin, her biri kendi birleştirme değerleriyle, ve API her alıcıyı kendi birleştirilmiş metniyle fiyatlandırır, tıpkı konsol gibi. Maliyeti öğrenmek için önce aynı metinle bir teklif isteyin; teklifle hiçbir şey gönderilmez veya ücretlendirilmez. Zamanlar ISO 8601 UTC formatında, para beş ondalık basamaklı ABD doları cinsinden bir dizedir.
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;Kimlik Doğrulama
Konsolda API ve webhooklar altında bir anahtar oluşturun; anahtarlar ilk kredili yüklemenizden sonra açılır. Her istekte Bearer token olarak gönderin. Bir anahtar bir kez gösterilir; en fazla 5 anahtar tutabilir ve herhangi birini anında iptal edebilirsiniz.
Authorization: Bearer smsb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxFiyatlar
Herkese açık, anahtar gerekmez. Her hedef için SMS segmenti başına fiyat veya bir ISO kodu için.
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"
}Kredi
GEThttps://smsbulk.cc/rest/v1/credit
{ "credit": "125.40000", "currency": "USD", "account": "A418820037715" }Teklif
Göndermeden bir gönderiyi fiyatlandırır. Gönder ile aynı metni alır.
POSThttps://smsbulk.cc/rest/v1/quote
| Alan | Tür | Anlam |
|---|---|---|
| to | string or string[] | Bir numara veya liste. + ile uluslararası form en güvenlisidir; yerel numaralar default_country gerektirir. |
| recipients | object[] | to yerine: { to, fields } listesi, burada fields {{name}} gibi birleştirme alanlarını doldurur. |
| text | string | Mesaj, birleştirme sonrası 2,000 karakter ve 10 segment kadar olabilir. |
| default_country | string | + olmadan yazılan numaralar için kullanılan ISO kodu, örneğin GB. Ayarlarda kayıtlı varsayılana döner. |
| opt_out | boolean | Kaydedilmiş devre dışı bırakma metnini son satır olarak ekler (önce Ayarlar'a kaydedin). |
{
"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önder
Bir kampanya oluşturur, toplamını kredinizden alır ve mesajları sıraya koyar. Kampanya ile 201 yanıtı verir. Gönderilemeyen numaralar left_out içinde listelenir ve asla ücretlendirilmez.
POSThttps://smsbulk.cc/rest/v1/send
| Alan | Tür | Anlam |
|---|---|---|
| to / recipients / text / default_country / opt_out | Quote'daki gibi. | |
| send_at | string | Başlangıç için ISO 8601 zamanı, 5 dakika ile 90 gün öncesi. Şimdi göndermek için boş bırakın. |
| name | string | Kampanya için etiketiniz, en fazla 80 karakter. |
| client_ref | string | Her mesaj ve webhook'ta dönen, en fazla 64 karakterlik kendi referansınız. |
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": []
}Kampanyalar
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}
Tek bir kampanya, durum başına sayıları içerir: sıraya alındı, gönderildi, teslim edildi, başarısız oldu ve iptal edildi. Mesaj listeleri sayfası after ve has_more ile; sayfa başına en fazla 1,000.
Mesajlar
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"
}Planlanmış bir kampanyayı iptal et
POSThttps://smsbulk.cc/rest/v1/campaigns/{id}/cancel
Başlangıç zamanına kadar çalışır. Tüm maliyet bir defter satırında kredinize iade edilir ve kampanya durumu iptal edilir olur.
Webhooklar
Konsolda bir endpoint URL'si ayarlayın. message.delivered, message.failed ve campaign.done için JSON POST ederiz, artık Test olayı gönder'e bastığınızda test. Her olay dört başlık taşır ve 10 saniye içinde herhangi bir 2xx yanıtı alındı olarak sayılır.
| Alan | Tür | Anlam |
|---|---|---|
| Smsbulk-Event | başlık | message.delivered gibi etkinlik adı. |
| Smsbulk-Delivery | başlık | Teslimat kimliği, bir etkinliğin her yeniden denemesinde aynıdır. |
| Smsbulk-Timestamp | başlık | Bu girişimin imzalandığı Unix saniyesi. |
| Smsbulk-Signature | başlık | sha256=, ardından zaman damgası, yeni satır ve ham gövdenin sbw_ gizli anahtarıyla anahtarlanmış base64 HMAC-SHA256'sı. |
Teslimat 8 kez denenir: hemen, sonra 1 dakika, 5 dakika, 15 dakika, 1 saat, 3 saat, 6 saat ve 12 saat sonra. Her deneme konsol teslimat günlüğünde HTTP durumu ve yanıtınızın başlangıcıyla gösterilir.
{
"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"
}
}İmzayı doğrula
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", ""))Hatalar ve Limitler
Hatalar HTTP durumu ve bir JSON gövdesi kullanır: {"error":{"code":"insufficient_credit","message":"..."}}. İstek başarısız olduğunda hiçbir ücret alınmaz.
| Alan | Tür | Anlam |
|---|---|---|
| 400 bad_json | Gövde JSON değil. | |
| 401 unauthorized | Eksik, hatalı biçimlendirilmiş veya iptal edilmiş anahtar. | |
| 402 insufficient_credit | Toplam tutar bakiyenizden fazla. Konsoldan bakiye yükleyin. | |
| 404 not_found | Bu hesapta böyle bir endpoint, kampanya, mesaj veya ISO kodu yok. | |
| 409 not_scheduled | Başlamış bir kampanya için iptal talebi yapıldı. | |
| 422 text_missing, text_too_long, too_many_segments | Mesaj boş, 2,000 karakterden uzun veya bir alıcı için 10 segmentten fazla. | |
| 422 no_recipients, too_many, recipients_missing | Gönderilebilir numara yok, 8,000 numaradan fazla veya ne to ne de recipients alanı doldurulmuş. | |
| 422 schedule_range, bad_send_at | send_at 5 dakika ile 90 gün aralığı dışında veya geçerli bir zaman değil. | |
| 422 opt_out_unset | opt_out true ancak Ayarlar'da kayıtlı bir metin yok. | |
| 429 rate_limited | Bu anahtar için dakikada 300 istekten fazla. |
Her cevap X-RateLimit-Limit, X-RateLimit-Remaining ve X-RateLimit-Reset taşır.
Dışarıda bırakılan kodlar
Fiyatlandırmadan önce kaldırılan numaralar, listedeki konumları, gönderdiğiniz değer, bir kod ve basit neden ile left_out içinde geri döner.
| Alan | Tür | Anlam |
|---|---|---|
| no_country_code | + işareti ve varsayılan ülke yok. | |
| too_short / too_long | 8 haneden az veya 15 haneden fazla. | |
| not_a_number | Numaranın bir parçası olmayan harfler veya semboller. | |
| nanp_length | On bir hanesi olmayan bir +1 numarası. | |
| not_covered | Listemizde olmayan bir hedef. | |
| duplicate | Aynı numara istekte daha önce geçti. | |
| suppressed | Numara göndermeme listenizde. |
Durumlar ve nedenler
| Alan | Tür | Anlam |
|---|---|---|
| queued | durum | Kuyruktan çıkmayı bekliyor. |
| sent | durum | Ağa teslim edildi, teslimat raporu bekleniyor. Çoğu rapor dakikalar içinde gelir; bazı ağlar 24 saate kadar sürebilir. |
| delivered | durum | Ağ, telefonun mesajı aldığını onayladı. |
| failed | durum | Teslim edilmedi. Tutar bakiyenize iade edildi. |
| canceled | durum | İptal ettiğiniz için gönderilmedi. İade edildi. |
| Alan | Tür | Anlam |
|---|---|---|
| network_error | sebep | Ağ hatası |
| switched_off | sebep | Telefon kapalı veya kapsama dışında |
| not_in_service | sebep | Numara hizmet dışında |
| blocked | sebep | Ağ veya alıcı tarafından engellendi |
| landline | sebep | Sabit hat veya SMS alamayan numara |
| content_filter | sebep | Ağın içerik filtresi tarafından reddedildi |
| expired | sebep | Süresi doldu: ağın zaman limiti içinde rapor alınamadı |
| unknown | sebep | Bilinmeyen ağ yanıtı |
| canceled | sebep | Gönderimden önce iptal edildi |
API hakkında sorularınız mı var? Konsolda bir bilet açın veya SSS'yi okuyun.