SSmartVTU
Developer API

SmartVTU API

Sell data, airtime, cable TV, electricity and exam pins from your own website or app. You pay from your wallet at your level's prices (this page shows Standard prices).

/api/v1
HTTPS + JSON IP whitelist Idempotent 60 req/min per key

Authentication

Create keys on your Developer page. Send the secret key as a Bearer token and the public key in X-API-Key. Keep the secret on your server only.

curl "/api/v1/balance" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx"
Add your server IP to the key's whitelist. Requests from other IPs get 403 ip_not_allowed, and a key with too many rejected requests is paused automatically.

Request signing optional

For extra safety, sign each request. When a key has "Require signed requests" on, unsigned calls are refused. Signed requests do not need the Bearer header.

FieldTypeDescription
X-API-Key requiredheaderYour public key.
X-Timestamp requiredheaderCurrent Unix time in seconds. Must be within 5 minutes of our clock.
X-Signature requiredheaderLowercase hex HMAC-SHA256 of timestamp + "\n" + METHOD + "\n" + path + "\n" + raw body, keyed with your secret. Path is the URL path, e.g. /api/v1/data.
# Build the signature in your language, then:
curl -X POST "/api/v1/data" \
  -H "X-API-Key: pk_live_xxxxxxxx" \
  -H "X-Timestamp: 1767787200" \
  -H "X-Signature: 5f2b...e9" \
  -H "Content-Type: application/json" \
  -d '{"network":"mtn","plan_id":12,"phone":"08012345678","reference":"order-1001"}'

Responses

Every response is JSON with the same shape. Check the HTTP code first, then code.

{
    "ok": true,
    "code": "ok",
    "message": "Human readable text",
    "data": {
        "...": "..."
    }
}

Status and error codes

HTTPcodeMeaning
200ok / successfulDone.
202pendingAccepted and processing. Poll the status endpoint; it ends as successful or refunded.
400failed / refundedThe order failed. Your wallet was refunded.
400bad_jsonThe body is not valid JSON.
401unauthorized / bad_signatureMissing or wrong key, or a bad signature.
402insufficient_balanceFund your wallet and retry with the same reference.
403key_disabled / ip_not_allowed / no_api_access / blockedThe key is paused, the IP is not whitelisted, your level has no API access, or our safety checks stopped the order.
404not_foundUnknown endpoint or reference.
409possible_duplicate / in_progressSame order seconds ago, or the same reference is still processing.
422validation_error / invalid_number / verification_unavailableFix the fields named in message.
429rate_limitedSlow down. Limit is per key per minute.
503paused / service_unavailable / api_disabledTemporarily unavailable. Retry later with the same reference.

Idempotent references

Every purchase needs your own reference. If your request times out, send it again with the same reference: you get the original order back with "replayed": true, and nothing is charged twice.

GET /balance

Your wallet balance in naira.

{
    "ok": true,
    "code": "ok",
    "message": "Wallet balance.",
    "data": {
        "balance": 15250.5,
        "referral_balance": 1200,
        "currency": "NGN",
        "wallet_status": "active"
    }
}

GET /services

Which services are on, network and decoder codes, disco codes, exam codes and prices, airtime discount and limits for your account.

curl "/api/v1/services" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx"

GET /plans

Data or cable plans with your price. Use plan_id when buying.

FieldTypeDescription
servicestringdata (default) or cable.
networkstringData: mtn, airtel, glo, 9mobile. Cable: dstv, gotv, startimes.
curl "/api/v1/plans?service=data&network=mtn" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx"
{
    "ok": true,
    "code": "ok",
    "message": "1 plan(s).",
    "data": {
        "service": "data",
        "plans": [
            {
                "plan_id": 12,
                "name": "MTN SME 1GB",
                "network": "mtn",
                "data_type": "sme",
                "size": "1GB",
                "validity": "30 days",
                "price": 255
            }
        ]
    }
}

POST /airtime

FieldTypeDescription
network requiredstringmtn, airtel, glo, 9mobile
phone requiredstring11-digit Nigerian number.
amount requiredintegerAirtime value in naira (50 to 50,000).
portedbooleanSet true when the number moved networks.
reference requiredstringYour own unique ID for this order (4 to 64 characters: letters, numbers, - _ .). Sending the same reference again never charges twice.
allow_duplicatebooleanSet true to buy the same thing for the same number again within a few minutes.
curl -X POST "/api/v1/airtime" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"network":"mtn","phone":"08012345678","amount":500,"reference":"order-1000"}'

POST /data

FieldTypeDescription
network requiredstringmtn, airtel, glo, 9mobile
plan_id requiredintegerFrom live plan IDs or GET /plans.
phone requiredstring11-digit Nigerian number.
portedbooleanNumber moved networks.
reference requiredstringYour own unique ID for this order (4 to 64 characters: letters, numbers, - _ .). Sending the same reference again never charges twice.
allow_duplicatebooleanSet true to buy the same thing for the same number again within a few minutes.
curl -X POST "/api/v1/data" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"network":"mtn","plan_id":12,"phone":"08012345678","reference":"order-1001"}'
{
    "ok": true,
    "code": "successful",
    "message": "Order completed.",
    "data": {
        "reference": "TX24110712AB9F",
        "client_reference": "order-1001",
        "service": "data",
        "network": "mtn",
        "data_type": "sme",
        "plan_id": 12,
        "recipient": "08012345678",
        "description": "MTN SME 1GB to 08012345678",
        "amount": 255,
        "face_value": 255,
        "status": "successful",
        "message": "",
        "token": null,
        "channel": "api",
        "created_at": "2026-11-07 12:00:00",
        "completed_at": "2026-11-07 12:00:03"
    }
}

POST /cable

FieldTypeDescription
cable requiredstringdstv, gotv, startimes
plan_id requiredintegerPackage ID from GET /plans?service=cable.
iuc requiredstringSmartcard / IUC number.
phonestringCustomer phone (optional).
confirm_unverifiedbooleanBuy even when the IUC cannot be verified right now.
reference requiredstringYour own unique ID for this order (4 to 64 characters: letters, numbers, - _ .). Sending the same reference again never charges twice.
allow_duplicatebooleanSet true to buy the same thing for the same number again within a few minutes.
curl -X POST "/api/v1/cable" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"cable":"gotv","plan_id":40,"iuc":"7012345678","reference":"order-1002"}'

POST /electricity

FieldTypeDescription
disco requiredstringikeja-electric, eko-electric, abuja-electric
meter requiredstringMeter number.
meter_typestringprepaid (default) or postpaid.
amount requiredintegerUnits value in naira.
phonestringCustomer phone (optional).
confirm_unverifiedbooleanBuy even when the meter cannot be verified right now.
reference requiredstringYour own unique ID for this order (4 to 64 characters: letters, numbers, - _ .). Sending the same reference again never charges twice.
allow_duplicatebooleanSet true to buy the same thing for the same number again within a few minutes.
curl -X POST "/api/v1/electricity" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"disco":"ikeja-electric","meter":"45012345678","meter_type":"prepaid","amount":5000,"reference":"order-1003"}'

Prepaid tokens come back in data.token once the order is successful.

POST /exam

FieldTypeDescription
exam requiredstringwaec, neco, nabteb
quantityinteger1 to 5.
reference requiredstringYour own unique ID for this order (4 to 64 characters: letters, numbers, - _ .). Sending the same reference again never charges twice.
allow_duplicatebooleanSet true to buy the same thing for the same number again within a few minutes.
curl -X POST "/api/v1/exam" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"exam":"waec","quantity":1,"reference":"order-1004"}'

POST /verify/meter and /verify/smartcard

Check the customer name before you buy. Meter: disco, meter, meter_type. Smartcard: cable, iuc.

curl -X POST "/api/v1/verify/smartcard" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"cable":"dstv","iuc":"7012345678"}'
{
    "ok": true,
    "code": "ok",
    "message": "Verified.",
    "data": {
        "customer_name": "ADA OBI"
    }
}

GET /transactions/{reference}

Use our reference or yours. Poll every 10 to 30 seconds while the status is pending.

curl "/api/v1/transactions/order-1001" \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "X-API-Key: pk_live_xxxxxxxx"

GET /transactions

FieldTypeDescription
statusstringsuccessful, pending, failed or refunded.
servicestringairtime, data, cable, electricity, exam, sms.
channelstringapi to show only API orders.
from / todateYYYY-MM-DD.
page / limitintegerUp to 100 per page.

Live plan IDs

Updated automatically, prices for Standard.

No plans published yet

Plans show here as soon as they are added.