अवलोकन
जब आपके अपने सिस्टम द्वारा संदेश ट्रिगर किए जाते हैं तो API का उपयोग करें: दुकान से ऑर्डर अपडेट, लॉगिन फॉर्म से कोड, बुकिंग टूल से रिमाइंडर। POST /v1/messages को 10,000 नंबरों तक का अनुरोध एक कैंपेन बनाता है, बैलेंस से शुल्क लेता है और हर संदेश को कतार में डालता है। API द्वारा बनाए गए कैंपेन भी कंसोल में उनके डिलीवरी रिपोर्ट के साथ दिखाई देते हैं।
- सभी राशियाँ US डॉलर में पाँच दशमलव स्थानों वाली स्ट्रिंग के रूप में हैं, उदाहरण के लिए "0.00534"।
- फ़ोन नंबर अंतरराष्ट्रीय हैं, + के साथ या बिना; स्पेस और डैश हटा दिए जाते हैं।
- समय UTC में ISO 8601 प्रारूप में हैं, उदाहरण के लिए 2026-12-01T09:00:00Z।
- संदेश और कैंपेन आईडी MSG-XXXX-XXXX-XXXX और CMP-XXXX-XXXX-XXXX जैसे दिखते हैं।
प्रमाणीकरण
कंसोल में API के तहत एक API कुंजी बनाएँ (कुंजियाँ आपके पहले टॉप-अप के बाद खुलती हैं)। इसे हर अनुरोध के Authorization हेडर में भेजें। एक कुंजी bso_api_ के बाद 36 वर्णों जैसी दिखती है; यह एक बार दिखाई जाती है, इसलिए इसे अपने सर्वर पर गुप्त रूप से स्टोर करें। नई कुंजी बनाने से पिछली कुंजी तुरंत बदल जाती है।
Authorization: Bearer bso_api_0123456789abcdef0123456789abcdef0123 Content-Type: application/json
सीमाएँ
| अनुरोध | प्रति कुंजी 300 प्रति मिनट; अधिक होने पर 429 rate_limited उत्तर |
|---|---|
| प्रति अनुरोध नंबर | 10,000 |
| संदेश पाठ | लिखे गए 2,000 वर्ण, 10 सेगमेंट तक |
| शेड्यूलिंग | send_at 5 मिनट से 60 दिन आगे तक |
| भेजने की गति | प्रति कतार 400 संदेश प्रति मिनट |
| वेबहुक पुनः प्रयास | लगभग 22 घंटे में 9 प्रयास |
संदेश भेजें
POST/v1/messages
एक टेक्स्ट को एक या अधिक नंबरों पर भेजता है। अनुरोध स्वीकार होने पर पूरी लागत बैलेंस से ली जाती है; प्रत्येक संदेश जो डिलीवर नहीं हो सका, बाद में रिफंड किया जाता है। उत्तर में स्टेटस 201 और प्रत्येक संदेश के साथ कैंपेन होता है।
| फ़ील्ड | प्रकार | विवरण |
|---|---|---|
toआवश्यक | string | string[] | एक नंबर या अंतरराष्ट्रीय फॉर्मेट में 10,000 तक नंबरों की एक सरणी। दोहराए गए नंबर एक बार भेजे जाते हैं; पढ़ने योग्य नहीं होने वाले skipped में सूचीबद्ध होते हैं। |
textआवश्यक | string | संदेश, 2,000 वर्णों और 10 सेगमेंट्स तक। GSM-7 टेक्स्ट प्रति SMS में 160 वर्ण रखता है, यूनिकोड 70। |
send_atवैकल्पिक | string | भेजने का समय निर्धारित करने के लिए ISO 8601 समय, 5 मिनट से 60 दिन आगे। अभी भेजने के लिए इसे छोड़ दें। |
opt_outवैकल्पिक | boolean | true आपकी ऑप्ट-आउट लाइन को कंसोल सेटिंग्स से अंत में एक नई लाइन पर जोड़ता है। |
client_refवैकल्पिक | string | आपका अपना संदर्भ, 64 अक्षरों, अंकों और _ . : - तक। यह रिपोर्ट्स और वेबहुक्स में वापस आता है। |
curl -X POST https://bulksmsonline.cc/api/v1/messages \
-H "Authorization: Bearer $BSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to":["+447700900412","+33612345678"],"text":"Order 4521 has shipped. Track it at shop.example/t/4521","client_ref":"order-4521"}'const res = await fetch("https://bulksmsonline.cc/api/v1/messages", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.BSO_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ to: ["+447700900412", "+33612345678"], text: "Order 4521 has shipped.", client_ref: "order-4521" }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const campaign = await res.json();import os, requests
res = requests.post("https://bulksmsonline.cc/api/v1/messages",
headers={"Authorization": f"Bearer {os.environ['BSO_API_KEY']}"},
json={"to": ["+447700900412", "+33612345678"], "text": "Order 4521 has shipped.", "client_ref": "order-4521"},
timeout=30)
res.raise_for_status()
campaign = res.json()<?php
$ch = curl_init("https://bulksmsonline.cc/api/v1/messages");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BSO_API_KEY"), "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["to" => ["+447700900412", "+33612345678"], "text" => "Order 4521 has shipped.", "client_ref" => "order-4521"])]);
$campaign = json_decode(curl_exec($ch), true);HTTP/1.1 201 Created
{
"id": "CMP-7KQ2-M9XA-4HTD",
"status": "sending",
"channel": "api",
"recipients": 2,
"segments": 1,
"encoding": "GSM-7",
"cost": "0.02158",
"refunded": "0.00000",
"client_ref": "order-4521",
"send_at": null,
"counts": { "queued": 2, "submitted": 0, "delivered": 0, "failed": 0, "canceled": 0 },
"skipped": [],
"balance": "118.43120",
"messages": [
{ "id": "MSG-3RTW-81KD-ZQ0P", "to": "+447700900412", "country": "GB", "segments": 1, "price": "0.00834", "status": "queued" },
{ "id": "MSG-5HNA-4C2E-7YVB", "to": "+33612345678", "country": "FR", "segments": 1, "price": "0.01324", "status": "queued" }
]
}भेजने का अनुमान लगाएँ
POST/v1/quote
/v1/messages के समान बॉडी लेता है और बिना कुछ भेजे या चार्ज किए सटीक लागत, सेगमेंट्स और एन्कोडिंग लौटाता है। अपने उपयोगकर्ताओं को कन्फर्म करने से पहले कीमत दिखाने के लिए इसका उपयोग करें।
{
"recipients": 2,
"skipped": [],
"segments": 1,
"encoding": "GSM-7",
"cost": "0.02158",
"balance": "118.45278",
"countries": [ { "country": "GB", "recipients": 1, "cost": "0.00834" }, { "country": "FR", "recipients": 1, "cost": "0.01324" } ]
}बैलेंस
GET/v1/balance
{ "balance": "118.43120", "currency": "USD" }दरें
GET/v1/rates
GET/v1/rates/{country}
प्रत्येक गंतव्य के लिए, या एक दो-अक्षर ISO देश कोड के लिए एक SMS सेगमेंट की कीमत। ये दोनों एंडपॉइंट्स सार्वजनिक हैं और किसी कुंजी की आवश्यकता नहीं है।
{ "country": "DE", "name": "Germany", "calling_code": "+49", "price": "0.01544", "currency": "USD", "per": "segment" }संदेश स्थिति
GET/v1/messages/{id}
{
"id": "MSG-3RTW-81KD-ZQ0P",
"campaign_id": "CMP-7KQ2-M9XA-4HTD",
"to": "+447700900412",
"country": "GB",
"segments": 1,
"price": "0.00834",
"status": "delivered",
"error": null,
"created_at": "2026-11-07T09:00:02Z",
"submitted_at": "2026-11-07T09:00:03Z",
"final_at": "2026-11-07T09:00:08Z"
}कैंपेन
GET/v1/campaigns/{id}
GET/v1/campaigns/{id}/messages?offset=0&limit=100
पहला स्टेटस के अनुसार गिनती के साथ कैंपेन लौटाता है; दूसरा इसके संदेशों के माध्यम से पेज करता है, प्रति पेज 1,000 तक। कंसोल से भेजे गए कैंपेन को उनके पेज पर दिखाए गए id के साथ उसी तरह पढ़ा जा सकता है।
निर्धारित भेजना रद्द करें
POST/v1/campaigns/{id}/cancel
एक कैंपेन को रोकता है जो send_at के साथ बनाया गया था और अभी तक शुरू नहीं हुआ है। प्रत्येक संदेश रद्द कर दिया जाता है और पूरी लागत बैलेंस में वापस आ जाती है। एक कैंपेन जो पहले से ही भेज रहा है, 409 not_scheduled का उत्तर देता है।
डिलीवरी वेबहुक्स
प्रत्येक अंतिम स्टेटस (डिलीवर्ड या फेल्ड) को POST अनुरोध के रूप में प्राप्त करने के लिए कंसोल में API के तहत एक HTTPS URL जोड़ें। 8 सेकंड के भीतर किसी भी 2xx स्टेटस के साथ उत्तर दें। अन्य उत्तर और टाइमआउट 20 सेकंड, 1, 5 और 15 मिनट, फिर 1, 3, 6 और 12 घंटे के बाद पुनः प्रयास किए जाते हैं, कुल 9 प्रयास। एक ही इवेंट दो बार आ सकता है, इसलिए इसे एक बार प्रोसेस करने के लिए event_id का उपयोग करें।
POST https://your-server.example/sms-status
Content-Type: application/json
X-BulkSMSOnline-Signature: t=1794042008,v1=5f0c2a...e91b
{
"type": "message.status",
"event_id": "ev_8f1c22d0a4b97e63c1f0",
"sent_at": "2026-11-07T09:00:09Z",
"data": {
"message_id": "MSG-3RTW-81KD-ZQ0P",
"campaign_id": "CMP-7KQ2-M9XA-4HTD",
"to": "+447700900412",
"country": "GB",
"status": "delivered",
"error": null,
"segments": 1,
"price": "0.00834",
"currency": "USD",
"client_ref": "order-4521",
"final_at": "2026-11-07T09:00:08Z"
}
}सिग्नेचर सत्यापित करें
हेडर में Unix समय t और सिग्नेचर v1 = आपके वेबहुक सीक्रेट के साथ स्ट्रिंग "t.raw_body" का HMAC-SHA256, हेक्स में होता है। JSON को पार्स करने से पहले raw बॉडी पर इसकी गणना करें, निरंतर समय में तुलना करें और पांच मिनट से पुराने इवेंट्स को अस्वीकार करें।
import crypto from "node:crypto";
export function verify(rawBody, 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 = crypto.createHmac("sha256", secret).update(m[1] + "." + rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(m[2]));
}import hmac, hashlib, re, time
def verify(raw_body: 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_body, 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)) return false;
if (abs(time() - (int)$m[1]) > 300) return false;
$want = hash_hmac('sha256', $m[1] . '.' . $raw, $secret);
return hash_equals($want, $m[2]);
}स्थितियाँ
| queued | स्वीकृत और कतार छोड़ने की प्रतीक्षा कर रहा है |
|---|---|
| submitted | गंतव्य नेटवर्क को सौंपा गया |
| delivered | हैंडसेट द्वारा पुष्टि की गई (अंतिम) |
| failed | डिलीवर नहीं किया जा सका; कीमत रिफंड की जाती है (अंतिम) |
| canceled | भेजने से पहले रद्द किए गए शेड्यूल्ड कैंपेन का हिस्सा (अंतिम) |
कैंपेन स्टेटस scheduled, sending, complete या canceled है।
त्रुटियाँ
त्रुटियाँ HTTP स्टेटस और JSON बॉडी के साथ उत्तर देती हैं: { "error": { "code": "insufficient_balance", "message": "..." } }। जब कोई अनुरोध त्रुटि के साथ उत्तर देता है तो कुछ भी चार्ज नहीं किया जाता है।
| HTTP | कोड | अर्थ |
|---|---|---|
| 400 | invalid_request | कोई फ़ील्ड गायब है या मान्य नहीं है; संदेश बताता है कि कौन सा। |
| 400 | invalid_json | बॉडी JSON नहीं है। |
| 400 | no_valid_numbers | कोई भी नंबर पढ़ा नहीं जा सका। |
| 401 | unauthorized | कुंजी गायब, गलत फॉर्मेट में या बदली गई है। |
| 402 | insufficient_balance | बैलेंस लागत को कवर नहीं करता; टॉप-अप करें और फिर से भेजें। |
| 404 | not_found | अज्ञात आईडी या एंडपॉइंट। |
| 409 | not_scheduled | केवल एक शेड्यूल्ड कैंपेन जो शुरू नहीं हुई है, रद्द की जा सकती है। |
| 413 | too_many_recipients | एक अनुरोध में 10,000 से अधिक नंबर। |
| 429 | rate_limited | एक मिनट में 300 से अधिक अनुरोध। |
| 500 | server_error | अनुरोध दोबारा करें; कुछ भी चार्ज नहीं किया गया। |