Powerful REST API for developers to integrate bulk SMS into their applications. Complete documentation with code examples in multiple languages.
Skip the setup! Get complete, production-ready code examples in PHP, Python, JavaScript, Node.js, and more. Plus download our Postman collections to test instantly.
Register for free and get your API token instantly. No credit card required.
Use our code examples in PHP, Python, JavaScript, or cURL
Make your first request and start sending bulk SMS
All API requests should be made to the following base URLs:
https://www.bulksmsnigeria.com/api/v2
https://www.bulksmsnigeria.com/api/sandbox/v2
Use our sandbox environment to test your integration without sending real SMS messages or consuming your wallet balance.
Messages are simulated - no actual SMS delivered
Your wallet balance is never deducted
Identical request/response format
| Endpoint | Method | Description |
|---|---|---|
/api/sandbox/v2/sms |
POST/GET | Send test SMS (simulated) |
/api/sandbox/v2/balance |
GET | Check account balance |
/api/sandbox/v2/delivery-reports |
GET | List delivery reports |
/api/sandbox/v2/sender-ids |
GET | List registered sender IDs |
/api/sandbox/v2/account |
GET | Get account information |
Our API accepts authentication tokens in multiple ways for maximum flexibility. The recommended method is using the Authorization header:
Authorization: Bearer YOUR_API_TOKEN
api_token: YOUR_API_TOKEN
?api_token=YOUR_API_TOKEN
{"api_token": "YOUR_API_TOKEN"}
Send bulk SMS messages to one or multiple recipients across all Nigerian networks
/api/v2/sms
Recommended
/api/v1/sms/create
Legacy
| Parameter | Type | Required | Description |
|---|---|---|---|
from |
string | Required | Sender ID (max 11 characters) |
to |
string | Required | Comma-separated phone numbers (e.g., 2347012345678,2348012345678) |
body |
string | Required | Message content (up to 1530 characters) |
gateway |
string | Optional | Gateway: direct-refund, direct-corporate, otp, dual-backup |
unicode |
boolean | Optional | Send as Unicode to keep emoji and non-Latin text (default: false). Unicode messages hold 70 characters per page instead of 160, so they cost more. When false, Unicode characters are replaced with standard equivalents (e.g. ’ becomes ') and emoji are removed. |
callback_url |
string | Optional | URL for delivery status callbacks/webhooks |
Unicode & billing
By default (unicode=false) your message is sent as standard SMS: 160 characters for one page, 153 per page after that. Curly quotes, dashes, accented letters and similar characters are converted to their plain equivalents and emoji are removed, so a stray character never pushes you onto Unicode pricing.
Set unicode=true (or 1) to send the text exactly as written. If it contains Unicode characters it is billed at 70 characters for one page and 67 per page after that. Emoji count as two characters.
curl -X POST https://www.bulksmsnigeria.com/api/v2/sms \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"from": "YourCompany",
"to": "2347012345678,2348012345678",
"body": "Hello from BulkSMS Nigeria API! Welcome to our service.",
"gateway": "direct-refund"
}'
{
"status": "success",
"code": "BSNG-0000",
"message": "Message sent successfully",
"data": {
"message_id": "a22f907b-c5aa-44e4-89e4-06fe253e9cbb",
"cost": 5.00,
"currency": "NGN",
"recipients_count": 2,
"gateway_used": "direct-refund",
"unicode": false,
"page_count": 1
}
}
Errors use the same envelope with "status": "error", a BSNG-XXXX code and a matching HTTP status. None of the errors below send the message or charge your wallet, so fix the request rather than retrying it unchanged.
{
"status": "error",
"code": "BSNG-2012",
"error": {
"message": "Duplicate message. An identical message was sent to the same recipients within the last minute, so this one was not sent and you were not charged.",
"code": "BSNG-2012",
"description": "Duplicate message"
}
}
| Code | HTTP | Meaning |
|---|---|---|
BSNG-2002 |
422 | The body is missing, or it only contains emoji/Unicode characters, which are removed when unicode is false. |
BSNG-2012 |
409 | Duplicate message: the same text was sent to the same recipients within the last minute. |
BSNG-2013 |
422 | Message not permitted on this route: OTPs, verification codes and other transactional messages must use the corporate/OTP route, and international senders the international route. Not sent and not charged. |
The full list of error codes is in the API documentation in your dashboard.
Check your current SMS credit balance
/api/v2/balance
curl -X GET https://www.bulksmsnigeria.com/api/v2/balance \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
{
"data": {
"status": "success",
"message": "Balance Inquiry Successful"
},
"balance": {
"total_balance": 9884.24,
"universal_wallet": "9879.23",
"sms_wallet": "5.01",
"sms_bonus": "0.00"
}
}
Get per-recipient delivery statuses for a sent message
/api/v2/delivery-reports
| Parameter | Required | Description |
|---|---|---|
message_id |
Yes | The message_id returned when the message was sent |
page |
No | Page number, defaults to 1 |
per_page |
No | Results per page, defaults to 100 (max 1000) |
curl -X GET "https://www.bulksmsnigeria.com/api/v2/delivery-reports?message_id=YOUR_MESSAGE_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
{
"status": "success",
"code": "BSNG-0000",
"message": "Delivery report retrieved",
"data": {
"message_id": "a22f907b-c5aa-44e4-89e4-06fe253e9cbb",
"summary": {"total": 3, "delivered": 2, "pending": 1, "failed": 0},
"reports": [
{"recipient": "2347037770033", "delivery_status": "delivrd"},
{"recipient": "2348012345678", "delivery_status": "delivrd"},
{"recipient": "2349050030090", "delivery_status": "sent awaiting status"}
],
"pagination": {"current_page": 1, "per_page": 100, "total": 3, "last_page": 1}
}
}
Get each recipient's delivery outcome pushed to your server, instead of polling for delivery reports
callback_url (and optionally your own customer_reference) when you send a message.callback_url.POST https://yourapp.com/webhooks/sms
Content-Type: application/json
{
"message_id": "a22f907b-c5aa-44e4-89e4-06fe253e9cbb",
"recipient": "2348012345678",
"delivery_status": "delivrd",
"customer_reference": "order-42",
"callback_url": "https://yourapp.com/webhooks/sms",
"data": {
"message_id": "a22f907b-c5aa-44e4-89e4-06fe253e9cbb",
"recipient": "2348012345678",
"delivery_status": "delivrd",
"customer_reference": "order-42"
}
}
| Field | Description |
|---|---|
message_id |
The message_id returned when you sent the message |
recipient |
The phone number this update is for, in international format |
delivery_status |
The final outcome, in lowercase (see below) |
customer_reference |
The reference you sent with the message, or null |
callback_url |
The URL this request was sent to |
data |
The same fields again, kept for older integrations. Read either; they always match |
delivery_status
Treat delivrd, delivered and success as delivered. Treat any other value as
not delivered: these are the network's failure codes (for example undeliv, expired,
rejectd, failed) or a status from us explaining what happened to the message.
New status values can appear over time, so match the three delivered values and treat everything else as not delivered rather than listing failure codes.
message_id + recipient).GET /api/v2/delivery-reports?message_id=… always has the latest status for every recipient.
Securing your endpoint: webhook requests are not signed. Put a long random secret in your callback URL
(for example https://yourapp.com/webhooks/sms?secret=…) and reject requests without it, and always use an HTTPS URL.
<?php
// https://yourapp.com/webhooks/sms?secret=YOUR_LONG_RANDOM_SECRET
if (!hash_equals('YOUR_LONG_RANDOM_SECRET', $_GET['secret'] ?? '')) {
http_response_code(403);
exit;
}
$update = json_decode(file_get_contents('php://input'), true);
$delivered = in_array($update['delivery_status'], ['delivrd', 'delivered', 'success'], true);
// Save idempotently: the same update can arrive more than once.
saveDeliveryStatus(
$update['message_id'],
$update['recipient'],
$delivered ? 'delivered' : 'failed',
$update['customer_reference']
);
http_response_code(200);
List your registered sender IDs and their approval status
/api/v2/sender-ids
curl -X GET https://www.bulksmsnigeria.com/api/v2/sender-ids \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
{
"status": "success",
"code": "BSNG-0000",
"message": "Sender IDs retrieved",
"data": {
"sender_ids": [
{"sender_id": "MyBrand", "status": "approved", "purpose": "Transactional alerts", "approved_at": "2026-01-15 09:30:00", "rejection_reason": null}
],
"total": 1
}
}
Retrieve your account profile, verification level, and balance
/api/v2/account
curl -X GET https://www.bulksmsnigeria.com/api/v2/account \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
{
"status": "success",
"code": "BSNG-0000",
"message": "Account information retrieved",
"data": {
"user_id": "0f8e2a64-1bbb-4f3e-9c0d-5a3a1c2d4e5f",
"email": "dev@example.com",
"name": "Ada Developer",
"account_status": "active",
"verification_level": "verified",
"balance": 10250.50,
"currency": "NGN",
"created_at": "2025-11-02 08:14:33"
}
}
Sign up now and get your API token instantly. Start sending bulk SMS in minutes with a ₦50 free SMS credit.
Messages delivered in seconds with 99.9% uptime guarantee
Deliver to MTN, GLO, Airtel, 9mobile including DND numbers
From ₦6.49 per SMS (excl. VAT) with volume discounts