Sell VTU from your own platform
Buy airtime, data, electricity and cable TV from your own website, app or bot, paid for from your SBB Data wallet at your own account's prices.
There are three ways in, and one API key works on all of them. Use the switch at the top of the page to see each endpoint in the API you're using.
| API | Use it when | Sells | Base URL |
|---|---|---|---|
| Native | You're building a new integration, or you already use the SBB Data API. | Data, airtime | https://www.sbbdata.com.ng/api/v1 |
| MSORG | Your system already uses an MSORG-style API. | Data, airtime, electricity, cable TV | https://www.sbbdata.com.ng/compat/msorg |
| ADEX | Your system already uses an ADEX-style API. | Data, airtime, electricity, cable TV | https://www.sbbdata.com.ng/compat/adex |
Fund your wallet the usual way, by bank transfer to your dedicated account in the dashboard. The API spends that same balance. There's no test mode, so every successful purchase is real.
Sandbox
Send a request to the live API from your browser. Requests marked [real money] debit your wallet and deliver for real.
What you type here goes only to the SBB Data API, and is never logged by this page. It's kept in this browser tab until you close it.
Authentication
Generate your API key in the dashboard at www.sbbdata.com.ng, under Profile → Account → API Integration. The same key works on all three APIs. Only the header changes:
| API | Header |
|---|---|
| Native | x-api-key: YOUR_API_KEY |
| MSORG | Authorization: Token YOUR_API_KEY |
| ADEX | Authorization: Token YOUR_API_KEY |
No transaction PIN is needed on API purchases, so anyone holding your key can spend your balance. Keep it on your server, never in website or app code. If it leaks, generate a new one in the dashboard straight away. That cancels the old key on all three APIs, so update your integrations at the same time.
Responses and errors
Every response is JSON. Check the HTTP status first, then the body.
Native
status is a boolean, and message explains what happened.
{
"status": false,
"message": "Invalid planId or network"
}MSORG and ADEX
Errors have the same shape on both. Branch on code, because message can change. When a purchase has been refunded, newbal gives your balance.
{
"status": "error",
"message": "mobile_number and plan are required.",
"code": "VALIDATION_ERROR"
}| HTTP | code (MSORG, ADEX) | Meaning |
|---|---|---|
400 | VALIDATION_ERROR, REQUEST_FAILED | Missing or wrong fields, low balance, a duplicate request, or a number that doesn't match the network. Nothing was charged, or any charge was refunded. |
400 | PROVIDER_REJECTED | The provider rejected a meter or smart card check. Nothing was charged. |
400 | PROVIDER_DECLINED | The provider declined an electricity or cable purchase. Your wallet has been refunded. |
401 | UNAUTHORIZED | Missing or wrong key, token or password. |
403 | FORBIDDEN | The account is frozen or temporarily locked. |
404 | NOT_FOUND | The endpoint or transaction doesn't exist on your account. |
429 | RATE_LIMITED | The airtime limit or a request limit was reached. Wait, then try again. |
500 | INTERNAL_ERROR | Something failed on our side. Look up the purchase before retrying. |
502 | PROVIDER_UNAVAILABLE | A provider check or listing failed. Nothing was charged. Try again shortly. |
When a purchase is still processing
Sometimes a provider doesn't give a clear answer straight away. The purchase is then marked processing and your wallet stays debited, because it may still be delivered. Don't retry it, or you could deliver twice. Look it up with the lookup endpoints until it settles. If it stays stuck, our team reviews it.
Limits and safeguards
| Rule | Detail |
|---|---|
| Airtime limit | ₦5,000 per account in any 20-minute window, on all three APIs. Reaching it returns 429 and pauses airtime for up to an hour. It can't be raised through the API, so spread out large batches. |
| Duplicate guard | The same purchase to the same number within 30 seconds is rejected with a 400, even if the first attempt failed. Wait before retrying. |
| Network check | A number whose prefix belongs to a different network is rejected before you're charged. For ported numbers, send bypassNetworkCheck (Native), Ported_number (MSORG) or bypass (ADEX). |
| Request limit | MSORG and ADEX allow 300 requests a minute. The ADEX token endpoint allows 20 a minute. |
| Password attempts | Five wrong passwords on the ADEX token endpoint lock the account for 5 minutes. |
Compatibility APIs
MSORG and ADEX let you move an existing integration to SBB Data by changing its base URL and token, with no other code changes. Choose MSORG or ADEX at the top of the page to read that API's setup. They cover data, airtime, electricity and cable TV. Exam pins, bulk SMS and printable recharge cards aren't offered.
MSORG
If your system already talks to an MSORG-style VTU API, point it at the base URL below and use your SBB Data API key as the token.
https://www.sbbdata.com.ng/compat/msorg
Network numbers
| Number | Network |
|---|---|
1 | MTN |
2 | Airtel |
3 | Glo |
4 | 9mobile |
The network names work too. Meter types are prepaid or 1, and postpaid or 2.
plan, disco_name, cablename and cableplan must come from List data plans, List discos and List providers on this site. Ids from your previous provider won't work. The network numbers above and the meter types are the only values that carry over.
The routes and request fields follow the MSORG convention, but the response bodies are SBB Data's own design. If your code reads specific response fields, try a small purchase and compare it with the samples here before moving live traffic.
status is true on account, listing and validation responses, and a string on purchases: "success" or "processing". Errors always have "status": "error". Check the HTTP code first, then the status documented for that endpoint.
ADEX
If your system already talks to an ADEX-style VTU API, point it at the base URL below. The two-step login works as you'd expect: exchange your email and password for a token, then send that token on every other call.
https://www.sbbdata.com.ng/compat/adex
- Get your token with
POST /api/userand HTTP Basic auth, using your account email and password. - Send it on every other call:
Authorization: Token <AccessToken>.
The AccessToken is your SBB Data API key. If you already have one it stays the same, so getting a token never breaks another integration on your account. You can also skip step 1 and use the key from your dashboard directly.
Network numbers
| Number | Network |
|---|---|
1 | MTN |
2 | Airtel |
3 | Glo |
4 | 9mobile |
The network names work too. meter_type is prepaid or 1, and postpaid or 2.
data_plan, disco, cablename and cableplan must come from List data plans, List discos and List providers on this site. Ids from your previous provider won't work. The network numbers above and the meter types are the only values that carry over.
Field names such as oldbal, newbal and wallet_vending follow the ADEX convention, but this is SBB Data's own implementation. If your code reads specific response fields, try a small purchase first.
Successful ADEX responses use the string "success" or "processing" in status. Errors have "status": "error".
Account
Check your account and wallet balance.
Account and token
Returns your wallet balance. On ADEX this is also how you get your token.
newBalance on every purchase, and your dashboard always shows it.Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/user/" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/user/", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"user": {
"id": "69070f2ea1b94c0012d4e8a1",
"username": "you@example.com",
"email": "you@example.com",
"full_name": "Your Name",
"wallet_balance": "15230.00"
}
}HTTP Basic, your account email and passwordSend your account email and password with HTTP Basic auth. Calling this again returns the same token, so it's safe to call from every server or worker you run, and to read your balance.
curl -X POST "https://www.sbbdata.com.ng/compat/adex/api/user" \ -u "you@example.com:your_account_password"
const credentials = btoa("you@example.com:your_account_password");
const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/user", {
method: "POST",
headers: { Authorization: `Basic ${credentials}` }
});
const data = await res.json();Response
{
"status": "success",
"AccessToken": "vtu_3f9a1b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e",
"balance": "15230.00",
"username": "you@example.com"
}Five wrong passwords lock the account for 5 minutes, the same as logging in on the website. If you store the token, you don't need to send your password on every run.
Data
List the plans you can buy, at your own prices, then buy one.
List data plans
Returns every active plan, grouped by network, with your account's pricing already applied. Prices can change, so refresh this list regularly instead of hardcoding it.
x-api-key: YOUR_API_KEYcurl "https://www.sbbdata.com.ng/api/v1/data-plans" \ -H "x-api-key: YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/api/v1/data-plans", {
headers: {
"x-api-key": "YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"data": {
"MTN": [
{
"planId": "159",
"name": "1GB - 30 Days",
"price": "300"
},
{
"planId": "160",
"name": "2GB - 30 Days",
"price": "550"
}
],
"AIRTEL": [
{
"planId": "10",
"name": "1GB - 30 Days",
"price": "300"
}
]
}
}Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/data-plans/" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/data-plans/", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"plans": {
"MTN": [
{
"planId": "159",
"name": "1GB - 30 Days",
"price": "300"
}
],
"AIRTEL": [
{
"planId": "10",
"name": "1GB - 30 Days",
"price": "300"
}
]
}
}Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/adex/api/data-plans" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/data-plans", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": "success",
"plans": {
"MTN": [
{
"planId": "159",
"name": "1GB - 30 Days",
"price": "300"
}
],
"AIRTEL": [
{
"planId": "10",
"name": "1GB - 30 Days",
"price": "300"
}
]
}
}Buy data
Debits your wallet and sends the plan to the number. If the provider declines, your wallet is refunded before the response comes back.
x-api-key: YOUR_API_KEYOne endpoint handles both data and airtime. Sending planId makes it a data purchase.
| Field | Type | Description | |
|---|---|---|---|
network | string | required | Network name (MTN, AIRTEL, GLO, 9MOBILE) or its number, 1 to 4. |
phone | string | required | The number to credit, e.g. 08031234567. |
planId | string | required | From List data plans. plan_id also works. |
bypassNetworkCheck | boolean | optional | Skips the check that the number belongs to the network. Use it for ported numbers. |
curl -X POST "https://www.sbbdata.com.ng/api/v1/purchase" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"network":"MTN","phone":"08031234567","planId":"159"}'const res = await fetch("https://www.sbbdata.com.ng/api/v1/purchase", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"network":"MTN","phone":"08031234567","planId":"159"})
});
const data = await res.json();Response
{
"status": true,
"message": "Data purchase successful",
"data": {
"reference": "API_DAT_9F2A1B4C0D3E",
"network": "MTN",
"phone": "08031234567",
"planId": "159",
"amount": 300,
"status": "successful",
"newBalance": 14930
}
}{
"status": true,
"message": "Your purchase is being processed. Check your transaction history for updates.",
"data": {
"reference": "API_DAT_9F2A1B4C0D3E",
"status": "processing",
"newBalance": 14930
}
}{
"status": false,
"message": "Transaction failed. Balance refunded.",
"data": {
"newBalance": 15230
}
}A 202 means the provider took the request but hasn't confirmed it yet. Your wallet stays debited, so don't retry. To check it from your own code, use the MSORG lookup with the reference and the same key.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
network | string or number | required | Network name (MTN, AIRTEL, GLO, 9MOBILE) or its number, 1 to 4. |
mobile_number | string | required | The number to credit, e.g. 08031234567. |
plan | string | required | From List data plans. |
Ported_number | boolean | optional | Skips the check that the number belongs to the network. |
curl -X POST "https://www.sbbdata.com.ng/compat/msorg/api/data/" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"network":1,"mobile_number":"08031234567","plan":"159"}'const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/data/", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"network":1,"mobile_number":"08031234567","plan":"159"})
});
const data = await res.json();Response
{
"status": "success",
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "API_DAT_9F2A1B4C0D3E",
"network": "MTN",
"mobile_number": "08031234567",
"plan": "159",
"amount": 300,
"message": "Data plan delivered to 08031234567.",
"oldbal": 15230,
"newbal": 14930
}{
"status": "error",
"message": "Transaction failed. Balance refunded.",
"code": "REQUEST_FAILED",
"newbal": 15230
}Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
network | string or number | required | Network name (MTN, AIRTEL, GLO, 9MOBILE) or its number, 1 to 4. |
phone | string | required | The number to credit, e.g. 08031234567. |
data_plan | string | required | From List data plans. |
request-id | string | optional | Your own reference. It's echoed back as sent. |
bypass | boolean | optional | Skips the check that the number belongs to the network. |
curl -X POST "https://www.sbbdata.com.ng/compat/adex/api/data" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"network":1,"phone":"08031234567","data_plan":"159","request-id":"order-1001"}'const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/data", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"network":1,"phone":"08031234567","data_plan":"159","request-id":"order-1001"})
});
const data = await res.json();Response
{
"network": "MTN",
"request-id": "order-1001",
"reference": "API_DAT_9F2A1B4C0D3E",
"amount": "300",
"dataplan": "159",
"status": "success",
"message": "Data delivered to 08031234567.",
"response": "Data delivered to 08031234567.",
"phone_number": "08031234567",
"oldbal": "15230",
"newbal": 14930,
"system": "API",
"plan_type": "GIFTING",
"wallet_vending": "wallet"
}{
"status": "error",
"message": "Transaction failed. Balance refunded.",
"code": "REQUEST_FAILED",
"newbal": 15230
}oldbal is a string and newbal is a number, matching what ADEX-style clients expect.
Airtime
Send airtime to any Nigerian number.
Buy airtime
Capped at ₦5,000 per account in any 20-minute window, on all three APIs. Any bonus balance on your account is spent before your wallet balance.
x-api-key: YOUR_API_KEYOne endpoint handles both data and airtime. Sending amount with no plan makes it an airtime purchase.
| Field | Type | Description | |
|---|---|---|---|
network | string | required | Network name (MTN, AIRTEL, GLO, 9MOBILE) or its number, 1 to 4. |
phone | string | required | The number to credit, e.g. 08031234567. |
amount | number | required | Amount in naira, greater than 0. |
bypassNetworkCheck | boolean | optional | Skips the check that the number belongs to the network. |
curl -X POST "https://www.sbbdata.com.ng/api/v1/purchase" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"network":"MTN","phone":"08031234567","amount":500}'const res = await fetch("https://www.sbbdata.com.ng/api/v1/purchase", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"network":"MTN","phone":"08031234567","amount":500})
});
const data = await res.json();Response
{
"status": true,
"message": "Transaction processed successfully.",
"data": {
"reference": "API_AIR_3C0430D93336",
"network": "MTN",
"phone": "08031234567",
"amount": 500,
"status": "successful",
"newBalance": 14730,
"newBonusBalance": 0
}
}{
"status": false,
"message": "Transaction failed. Balance refunded.",
"data": {
"newBalance": 15230,
"newBonusBalance": 0
}
}{
"status": false,
"message": "Transaction would exceed ₦5,000 airtime limit per 20 minutes. Please try again later."
}Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
network | string or number | required | Network name (MTN, AIRTEL, GLO, 9MOBILE) or its number, 1 to 4. |
mobile_number | string | required | The number to credit, e.g. 08031234567. |
amount | number | required | Amount in naira, greater than 0. |
Ported_number | boolean | optional | Skips the check that the number belongs to the network. |
curl -X POST "https://www.sbbdata.com.ng/compat/msorg/api/topup/" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"network":1,"mobile_number":"08031234567","amount":500}'const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/topup/", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"network":1,"mobile_number":"08031234567","amount":500})
});
const data = await res.json();Response
{
"status": "success",
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "API_AIR_3C0430D93336",
"network": "MTN",
"mobile_number": "08031234567",
"amount": 500,
"message": "₦500 airtime sent to 08031234567.",
"oldbal": 15230,
"newbal": 14730
}{
"status": "error",
"message": "Transaction would exceed ₦5,000 airtime limit per 20 minutes. Please try again later.",
"code": "RATE_LIMITED"
}Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
network | string or number | required | Network name (MTN, AIRTEL, GLO, 9MOBILE) or its number, 1 to 4. |
phone | string | required | The number to credit, e.g. 08031234567. |
amount | number | required | Amount in naira, greater than 0. |
request-id | string | optional | Your own reference. It's echoed back as sent. |
bypass | boolean | optional | Skips the check that the number belongs to the network. |
plan_type | string | optional | Echoed back as sent. Defaults to VTU. |
curl -X POST "https://www.sbbdata.com.ng/compat/adex/api/topup" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"network":1,"phone":"08031234567","amount":500,"request-id":"order-1002"}'const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/topup", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"network":1,"phone":"08031234567","amount":500,"request-id":"order-1002"})
});
const data = await res.json();Response
{
"network": "MTN",
"request-id": "order-1002",
"reference": "API_AIR_3C0430D93336",
"amount": 500,
"discount": 0,
"status": "success",
"message": "Successfully purchased MTN VTU ₦500 for 08031234567",
"phone_number": "08031234567",
"oldbal": "15230",
"newbal": 14730,
"system": "API",
"plan_type": "VTU",
"wallet_vending": "wallet"
}{
"status": "error",
"message": "Transaction would exceed ₦5,000 airtime limit per 20 minutes. Please try again later.",
"code": "RATE_LIMITED"
}Electricity
Available on MSORG and ADEX.
List discos
Returns the electricity companies you can pay, with the id each purchase needs.
Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/discos/" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/discos/", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"discos": [
{
"discoId": "DISCO_ID",
"name": "Ikeja Electric"
}
]
}Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/adex/api/discos" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/discos", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": "success",
"discos": [
{
"discoId": "DISCO_ID",
"name": "Ikeja Electric"
}
]
}Validate a meter
Returns the customer name on a meter so you can confirm it before paying.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
meternumber | string | required | The meter number. |
disconame | string | required | A discoId from List discos. |
mtype | string or number | required | prepaid or 1; postpaid or 2. |
amount | number | optional | The amount you plan to pay. Defaults to 1000. |
curl "https://www.sbbdata.com.ng/compat/msorg/api/validatemeter?meternumber=45031234567&disconame=DISCO_ID&mtype=prepaid&amount=2000" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/validatemeter?meternumber=45031234567&disconame=DISCO_ID&mtype=prepaid&amount=2000", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"customer_name": "ADA OBI",
"address": "1 Allen Avenue, Ikeja"
}Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
meter_number | string | required | The meter number. |
disco | string | required | A discoId from List discos. |
meter_type | string or number | required | prepaid or 1; postpaid or 2. |
amount | number | optional | The amount you plan to pay. Defaults to 1000. |
curl "https://www.sbbdata.com.ng/compat/adex/api/bill/bill-validation?meter_number=45031234567&disco=DISCO_ID&meter_type=prepaid&amount=2000" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/bill/bill-validation?meter_number=45031234567&disco=DISCO_ID&meter_type=prepaid&amount=2000", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": "success",
"name": "ADA OBI"
}Buy electricity
Validates the meter, debits your wallet, and pays the disco. Prepaid purchases return the token to give your customer. If the disco declines, your wallet is refunded before the response comes back.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
disco_name | string | required | A discoId from List discos. |
meter_number | string | required | The meter number. |
MeterType | string or number | required | prepaid or 1; postpaid or 2. |
amount | number | required | Whole naira. The disco may set its own minimum. |
curl -X POST "https://www.sbbdata.com.ng/compat/msorg/api/billpayment/" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"disco_name":"DISCO_ID","meter_number":"45031234567","MeterType":"prepaid","amount":2000}'const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/billpayment/", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"disco_name":"DISCO_ID","meter_number":"45031234567","MeterType":"prepaid","amount":2000})
});
const data = await res.json();Response
{
"status": "success",
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "COMPAT_ELEC_5b1f0c9ad2e34f77",
"disco_name": "DISCO_ID",
"meter_number": "45031234567",
"amount": 2000,
"token": "1234-5678-9012-3456-7890",
"units": "42.5",
"message": "Electricity payment successful.",
"oldbal": 15230,
"newbal": 13230
}{
"status": "processing",
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "COMPAT_ELEC_5b1f0c9ad2e34f77",
"disco_name": "DISCO_ID",
"meter_number": "45031234567",
"amount": 2000,
"message": "Electricity payment processing.",
"oldbal": 15230,
"newbal": 13230
}{
"status": "error",
"message": "Transaction declined by the provider. Your wallet has been refunded.",
"code": "PROVIDER_DECLINED",
"newbal": 15230
}Check status, not just the HTTP code. processing comes back with a 200: the provider hasn't given a clear answer, so your wallet stays debited while it's resolved. Don't retry; look it up instead.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
disco | string | required | A discoId from List discos. |
meter_number | string | required | The meter number. |
meter_type | string or number | required | prepaid or 1; postpaid or 2. |
amount | number | required | Whole naira. The disco may set its own minimum. |
request-id | string | optional | Your own reference. It's echoed back as sent. |
curl -X POST "https://www.sbbdata.com.ng/compat/adex/api/bill" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"disco":"DISCO_ID","meter_number":"45031234567","meter_type":"prepaid","amount":2000,"request-id":"order-1003"}'const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/bill", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"disco":"DISCO_ID","meter_number":"45031234567","meter_type":"prepaid","amount":2000,"request-id":"order-1003"})
});
const data = await res.json();Response
{
"disco_name": "DISCO_ID",
"request-id": "order-1003",
"reference": "COMPAT_ELEC_5b1f0c9ad2e34f77",
"amount": 2000,
"charges": 0,
"status": "success",
"message": "Transaction successful DISCO_ID PREPAID ₦2000 to 45031234567",
"meter_number": "45031234567",
"meter_type": "PREPAID",
"oldbal": "15230",
"newbal": 13230,
"system": "API",
"token": "1234-5678-9012-3456-7890",
"units": "42.5",
"wallet_vending": "wallet"
}{
"status": "error",
"message": "Transaction declined by the provider. Your wallet has been refunded.",
"code": "PROVIDER_DECLINED",
"newbal": 15230
}Check status, not just the HTTP code. processing comes back with a 200 and your wallet stays debited while it's resolved. Don't retry.
Cable TV
Available on MSORG and ADEX. Prices are always set by SBB Data. Any amount you send is ignored.
List providers and plans
Returns each cable provider with its plans and current prices.
Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/cable-providers/" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/cable-providers/", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"providers": [
{
"billerId": "BILLER_ID",
"name": "DStv",
"plans": [
{
"itemId": "ITEM_ID",
"name": "Compact",
"amount": 15700
}
]
}
]
}Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/adex/api/cable-providers" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/cable-providers", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": "success",
"providers": [
{
"billerId": "BILLER_ID",
"name": "DStv",
"plans": [
{
"itemId": "ITEM_ID",
"name": "Compact",
"amount": 15700
}
]
}
]
}Validate a smart card
Returns the customer name on a smart card or IUC number so you can confirm it before paying.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
smart_card_number | string | required | The smart card or IUC number. |
cablename | string | required | A billerId from List providers. |
curl "https://www.sbbdata.com.ng/compat/msorg/api/validateiuc?smart_card_number=7012345678&cablename=BILLER_ID" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/validateiuc?smart_card_number=7012345678&cablename=BILLER_ID", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"customer_name": "BELLO MUSA",
"renewal_amount": 15700
}renewal_amount is what renewing the current bouquet costs right now, when the provider reports one.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
iuc | string | required | The smart card or IUC number. |
cable | string | required | A billerId from List providers. |
curl "https://www.sbbdata.com.ng/compat/adex/api/cable/cable-validation?iuc=7012345678&cable=BILLER_ID" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/cable/cable-validation?iuc=7012345678&cable=BILLER_ID", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": "success",
"name": "BELLO MUSA"
}Buy a cable subscription
Send a plan to change the customer to that bouquet at its listed price. Leave the plan out to renew their current bouquet at the amount the provider quotes. If the provider declines, your wallet is refunded before the response comes back.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
cablename | string | required | A billerId from List providers. |
smart_card_number | string | required | The smart card or IUC number. |
cableplan | string | optional | An itemId to change to. Leave it out to renew. |
curl -X POST "https://www.sbbdata.com.ng/compat/msorg/api/cablesub/" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"cablename":"BILLER_ID","cableplan":"ITEM_ID","smart_card_number":"7012345678"}'const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/cablesub/", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"cablename":"BILLER_ID","cableplan":"ITEM_ID","smart_card_number":"7012345678"})
});
const data = await res.json();Response
{
"status": "success",
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "COMPAT_CABLE_8e2a7d10c4b94f33",
"cablename": "BILLER_ID",
"cableplan": "ITEM_ID",
"smart_card_number": "7012345678",
"amount": 15700,
"message": "Cable subscription successful.",
"oldbal": 30930,
"newbal": 15230
}{
"status": "error",
"message": "Transaction declined by the provider. Your wallet has been refunded.",
"code": "PROVIDER_DECLINED",
"newbal": 30930
}As with electricity, check status: processing comes back with a 200 and your wallet stays debited while it's resolved.
Authorization: Token YOUR_API_KEY| Field | Type | Description | |
|---|---|---|---|
cablename | string | required | A billerId from List providers. |
smart_card_number | string | required | The smart card or IUC number. |
cableplan | string | optional | An itemId to change to. Leave it out to renew. |
request-id | string | optional | Your own reference. It's echoed back as sent. |
curl -X POST "https://www.sbbdata.com.ng/compat/adex/api/cable" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"cablename":"BILLER_ID","cableplan":"ITEM_ID","smart_card_number":"7012345678","request-id":"order-1004"}'const res = await fetch("https://www.sbbdata.com.ng/compat/adex/api/cable", {
method: "POST",
headers: {
Authorization: "Token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({"cablename":"BILLER_ID","cableplan":"ITEM_ID","smart_card_number":"7012345678","request-id":"order-1004"})
});
const data = await res.json();Response
{
"cablename": "BILLER_ID",
"cableplan": "ITEM_ID",
"amount": 15700,
"status": "success",
"message": "Successfully subscribed BILLER_ID for Smart Card 7012345678",
"smart_card_number": "7012345678",
"request-id": "order-1004",
"reference": "COMPAT_CABLE_8e2a7d10c4b94f33",
"oldbal": 30930,
"newbal": 15230,
"system": "API"
}{
"status": "error",
"message": "Transaction declined by the provider. Your wallet has been refunded.",
"code": "PROVIDER_DECLINED",
"newbal": 30930
}As with electricity, check status: processing comes back with a 200 and your wallet stays debited while it's resolved.
Transactions
Check on purchases you've made. These live on MSORG, and your key works there whichever API you bought through.
List data purchases
Returns your 100 most recent data purchases, newest first.
Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/data/" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/data/", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"results": [
{
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "API_DAT_9F2A1B4C0D3E",
"network": "MTN",
"mobile_number": "08031234567",
"plan": "159",
"amount": 300,
"status": "success",
"created_at": "2026-11-02T10:12:03.000Z"
}
]
}plan here is the id of the plan as it was sent to the provider, which can differ from the planId you bought with.
Look up a data or airtime purchase
Takes the id or the reference of a purchase, so it also finds purchases made through the native API.
reference you got back.Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/data/API_DAT_9F2A1B4C0D3E" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/data/API_DAT_9F2A1B4C0D3E", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "API_DAT_9F2A1B4C0D3E",
"network": "MTN",
"mobile_number": "08031234567",
"plan": "159",
"amount": 300,
"status": "success",
"created_at": "2026-11-02T10:12:03.000Z"
}status is success, processing, pending or failed.
Look up an electricity purchase
Takes the id or reference of a purchase. Also how you get a prepaid token again.
Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/billpayment/COMPAT_ELEC_5b1f0c9ad2e34f77" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/billpayment/COMPAT_ELEC_5b1f0c9ad2e34f77", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "COMPAT_ELEC_5b1f0c9ad2e34f77",
"disco_name": "DISCO_ID",
"meter_number": "45031234567",
"amount": 2000,
"token": "1234-5678-9012-3456-7890",
"status_detail": "success"
}Here status only says the lookup worked. The purchase's own state is in status_detail.
Look up a cable purchase
Takes the id or reference of a purchase.
Authorization: Token YOUR_API_KEYcurl "https://www.sbbdata.com.ng/compat/msorg/api/cablesub/COMPAT_CABLE_8e2a7d10c4b94f33" \ -H "Authorization: Token YOUR_API_KEY"
const res = await fetch("https://www.sbbdata.com.ng/compat/msorg/api/cablesub/COMPAT_CABLE_8e2a7d10c4b94f33", {
headers: {
Authorization: "Token YOUR_API_KEY"
}
});
const data = await res.json();Response
{
"status": true,
"id": "69070f2ea1b94c0012d4e8a1",
"reference": "COMPAT_CABLE_8e2a7d10c4b94f33",
"cablename": "BILLER_ID",
"smart_card_number": "7012345678",
"amount": 15700,
"status_detail": "success"
}Here status only says the lookup worked. The purchase's own state is in status_detail.
API