API Reference
The BulkieMsg API lets you send high-volume SMS and WhatsApp messages, check your credit balance, and manage sender identities — from any language or platform.
All requests go to the base URL below. Every response is JSON. Every request body must include Content-Type: application/json.
https://bulkiemsg.com/api/v1
success boolean. On error, an error field describes what went wrong.
Authentication
Every request must include your API key. Generate one from your BulkieMsg dashboard under Settings → API Keys. Keys look like bm_live_xxxxxxxxxxxxxxxx.
Pass the key via the Authorization header (preferred) or X-API-Key:
Authorization: Bearer bm_live_your_api_key_here
X-API-Key: bm_live_your_api_key_here
Quick Start
Generate your API key
Log in, go to Settings → API Keys, and click Generate Key. Copy it — it's shown only once.
Get a Sender ID approved
Go to SMS → Sender IDs in your dashboard and request a branded ID (e.g. your company name). You cannot send SMS without an approved Sender ID.
Send your first message
curl -X POST https://bulkiemsg.com/api/v1/sms/send \ -H "Authorization: Bearer bm_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender_id": "YourBrand", "message": "Hello! Your order has shipped.", "to": ["233201234567"] }'
$ch = curl_init('https://bulkiemsg.com/api/v1/sms/send'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer bm_live_YOUR_KEY', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'sender_id' => 'YourBrand', 'message' => 'Hello! Your order has shipped.', 'to' => ['233201234567'], ]), ]); $res = json_decode(curl_exec($ch), true); var_dump($res);
import requests res = requests.post( "https://bulkiemsg.com/api/v1/sms/send", headers={ "Authorization": "Bearer bm_live_YOUR_KEY", "Content-Type": "application/json", }, json={ "sender_id": "YourBrand", "message": "Hello! Your order has shipped.", "to": ["233201234567"], } ) print(res.json())
const res = await fetch('https://bulkiemsg.com/api/v1/sms/send', { method: 'POST', headers: { 'Authorization': 'Bearer bm_live_YOUR_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ sender_id: 'YourBrand', message: 'Hello! Your order has shipped.', to: ['233201234567'], }), }); console.log(await res.json());
// Works in any browser environment async function sendSms(to, message) { const res = await fetch('https://bulkiemsg.com/api/v1/sms/send', { method: 'POST', headers: { 'Authorization': 'Bearer bm_live_YOUR_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ sender_id: 'YourBrand', message, to: Array.isArray(to) ? to : [to], }), }); const data = await res.json(); if (!data.success) { throw new Error(data.error || 'SMS send failed'); } return data; } // Usage sendSms('233201234567', 'Hello! Your order has shipped.') .then(d => console.log(d.message)) .catch(e => console.error(e.message));
Send SMS SMS
Send an SMS to one recipient or thousands. Pass a single phone number or an array. Credits are deducted immediately — 1 credit per SMS page per recipient.
Request Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| sender_id | string | required | Your approved Sender ID (e.g. MyBrand). Must be approved in your dashboard first. |
| message | string | required | The SMS body. 160 chars = 1 credit. Messages over 160 chars are split into pages (153 chars each). Unicode (emoji/Arabic) = 70 chars per page. |
| to | string | array | required | A phone number or array of phone numbers. Accepts 233XXXXXXXXX (E.164) or local 0XXXXXXXXX format — both are normalised automatically. |
curl -X POST https://bulkiemsg.com/api/v1/sms/send \ -H "Authorization: Bearer bm_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender_id": "MyBrand", "message": "Your OTP is 482910. Valid for 5 minutes.", "to": "233201234567" }' # Bulk — pass an array curl -X POST https://bulkiemsg.com/api/v1/sms/send \ -H "Authorization: Bearer bm_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender_id": "MyBrand", "message": "Flash sale — 30% off today only!", "to": ["233201234567", "233271234567", "0551234567"] }'
$payload = [ 'sender_id' => 'MyBrand', 'message' => 'Flash sale — 30% off today only!', 'to' => ['233201234567', '233271234567'], ]; $ch = curl_init('https://bulkiemsg.com/api/v1/sms/send'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => [ 'Authorization: Bearer bm_live_YOUR_KEY', 'Content-Type: application/json', ], ]); $result = json_decode(curl_exec($ch), true); curl_close($ch);
import requests r = requests.post( "https://bulkiemsg.com/api/v1/sms/send", headers={"Authorization": "Bearer bm_live_YOUR_KEY"}, json={ "sender_id": "MyBrand", "message": "Flash sale — 30% off today only!", "to": ["233201234567", "233271234567"], } ) print(r.json())
const r = await fetch('https://bulkiemsg.com/api/v1/sms/send', { method: 'POST', headers: { 'Authorization': 'Bearer bm_live_YOUR_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ sender_id: 'MyBrand', message: 'Flash sale — 30% off today only!', to: ['233201234567', '233271234567'], }), }); console.log(await r.json());
async function sendBulkSms(recipients, message, senderId) { const res = await fetch('https://bulkiemsg.com/api/v1/sms/send', { method: 'POST', headers: { 'Authorization': 'Bearer bm_live_YOUR_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ sender_id: senderId, message, to: Array.isArray(recipients) ? recipients : [recipients], }), }); if (!res.ok && res.status !== 402) { throw new Error(`HTTP error: ${res.status}`); } const data = await res.json(); if (!data.success) { throw new Error(data.error || 'Send failed'); } return data; } // Usage try { const result = await sendBulkSms( ['233201234567', '233271234567'], 'Flash sale — 30% off today only!', 'MyBrand' ); console.log(result.message); // "Successfully sent to 2 recipient(s)." } catch (e) { console.error('SMS error:', e.message); }
Responses
{
"success": true,
"message": "Successfully sent to 3 recipient(s)."
}{
"success": false,
"error": "Insufficient credits. Need 5, have 2."
}{
"success": false,
"error": "Invalid or revoked API key."
}Check Balance SMS
Returns the current SMS credit balance for the authenticated account. No request body required.
curl https://bulkiemsg.com/api/v1/sms/balance \ -H "Authorization: Bearer bm_live_YOUR_KEY"
$ch = curl_init('https://bulkiemsg.com/api/v1/sms/balance'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer bm_live_YOUR_KEY'], ]); $res = json_decode(curl_exec($ch), true);
import requests r = requests.get( "https://bulkiemsg.com/api/v1/sms/balance", headers={"Authorization": "Bearer bm_live_YOUR_KEY"} ) print(r.json())
const r = await fetch('https://bulkiemsg.com/api/v1/sms/balance', { headers: { 'Authorization': 'Bearer bm_live_YOUR_KEY' } }); console.log(await r.json());
async function getBalance() { const res = await fetch('https://bulkiemsg.com/api/v1/sms/balance', { headers: { 'Authorization': 'Bearer bm_live_YOUR_KEY' } }); const data = await res.json(); if (!data.success) throw new Error(data.error); return data.sms_credits; } // Usage getBalance() .then(credits => console.log(`Balance: ${credits} credits`)) .catch(e => console.error(e.message));
{
"success": true,
"sms_credits": 500
}Sender IDs SMS
All SMS must be sent with an approved Sender ID. Request one below — approval typically takes up to 48 hours.
Submit a new Sender ID for approval. IDs must be 3–11 alphanumeric characters with no spaces.
Request Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| sender_id | string | required | The Sender ID to request, e.g. MyBrand. 3–11 characters, no spaces. |
curl -X POST https://bulkiemsg.com/api/v1/sms/sender_ids \ -H "Authorization: Bearer bm_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender_id": "MyBrand" }'
{
"success": true,
"message": "Sender ID submitted for review.",
"sender_id_id": 42
}Returns all your Sender IDs and their current approval status (pending, approved, rejected).
curl https://bulkiemsg.com/api/v1/sms/sender_ids \ -H "Authorization: Bearer bm_live_YOUR_KEY"
{
"success": true,
"sender_ids": [
{
"id": 42,
"sender_id": "MyBrand",
"status": "approved",
"created_at": "2026-06-01 10:00:00"
}
]
}Send WhatsApp Message WhatsApp
Sends a plain text WhatsApp message to one or more recipients via your connected device. Costs 1 credit per recipient.
Request Parameters
| Parameter | Type | Description | |
|---|---|---|---|
| to | string | array | required | Phone number(s) in international format — 233XXXXXXXXX or local 0XXXXXXXXX. |
| message | string | required | The message text. Max 4096 characters. Supports line breaks. |
curl -X POST https://bulkiemsg.com/api/v1/whatsapp/send \ -H "Authorization: Bearer bm_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": "233201234567", "message": "Hello! Your appointment is confirmed for tomorrow at 10am." }'
{
"success": true,
"message": "Message sent to 1 recipient(s)."
}Link Device WhatsApp
Initiates a WhatsApp session and returns a QR code you can scan with your phone to link it. The session stays active until you unlink.
curl -X POST https://bulkiemsg.com/api/v1/whatsapp/session \ -H "Authorization: Bearer bm_live_YOUR_KEY"
{
"success": true,
"qr_code": "data:image/png;base64,iVBOR...",
"message": "Scan the QR code with WhatsApp on your phone.",
"expires_in": 60
}Display the qr_code base64 image to the user. The code expires in 60 seconds — call this endpoint again if it expires before scanning.
Rate Limits
The API enforces a limit of 60 requests per minute per API key. If you exceed this, you'll receive a 429 Too Many Requests response. Wait 60 seconds and retry.
{
"success": false,
"error": "Rate limit exceeded. Try again in 60 seconds.",
"retry_after": 60
}Errors & Codes
All errors return a JSON body with "success": false and an error string describing the problem. Use the HTTP status code to categorise the failure.
| HTTP Code | Meaning | Common cause |
|---|---|---|
| 200 | OK | Request succeeded. |
| 400 | Bad Request | Missing or invalid parameter — check error for details. |
| 401 | Unauthorized | API key missing, invalid, or revoked. |
| 402 | Payment Required | Not enough SMS credits to complete the request. |
| 404 | Not Found | The requested resource doesn't exist. |
| 405 | Method Not Allowed | Wrong HTTP verb — check GET vs POST. |
| 422 | Unprocessable | Validation failed — e.g. invalid phone number format. |
| 500 | Server Error | Internal error. Contact support if this persists. |
| 502 | Gateway Error | SMS gateway unreachable. Credits not deducted. Retry shortly. |
Common error messages
// Missing API key { "success": false, "error": "API key is required." } // Invalid key { "success": false, "error": "Invalid or revoked API key." } // Not enough credits { "success": false, "error": "Insufficient credits. Need 10, have 3." } // Sender ID not approved { "success": false, "error": "Sender ID 'MyBrand' is not approved." } // No valid recipients { "success": false, "error": "No valid recipients provided." }