Home / Developers

Developer API

Sell airtime, data, cable TV, electricity and exam PINs, send bank transfers and give customers their own account numbers, from your own app, with one API key. Every example on this page is a real answer from our API.

Contents

Introduction

The UnityBills API is a JSON REST API. You call it from your server with your API key; we pay from your wallet and answer straight away with the result.

Live base URLhttps://unitybills.ng
Sandbox base URL https://dev.unitybills.ng

Register on the sandbox and test with free test money.

  • Send Content-Type: application/json and Accept: application/json. Bodies and answers are JSON.
  • Amounts are in naira. Phone numbers are Nigerian numbers such as 08031234567.
  • Networks: 1 MTN, 2 GLO, 3 9MOBILE, 4 AIRTEL.
  • Every purchase is paid from your wallet. If it fails it is refunded at once; if the provider is still processing it, it is completed or refunded automatically.

Quick start

  1. Create an account and complete your business verification (KYB).
  2. Once your KYC is approved, sign the Service Agreement in your dashboard (Account → Service Agreement). That unlocks merchant and API access.
  3. Copy your API key from Dashboard → API and build against the sandbox: it answers exactly like live, with test money.
  4. Fund your live wallet and switch to the live base URL and key.
Your first call
curl -X GET 'https://unitybills.ng/user' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'

Authentication

Send your API key in the Authorization header of every request, as Token YOUR_API_KEY. Keep the key on your server: never put it in a mobile app, web page or public repository. You can generate a new key on Dashboard → API at any time (the old one stops working).

A missing or wrong key is answered with HTTP 401, for example:

{
    "status": "error",
    "Status": "failed",
    "error": "Invalid API key"
}

Sandbox

The sandbox is a separate copy of UnityBills where nothing real happens: no airtime, data, subscription, token or bank transfer is delivered and no real money moves. Everything else is the live system: the same validation, wallet debits and refunds, receipts, webhooks and exactly the same responses. Answers carry the header X-UnityBills-Mode: sandbox.

  1. Register on the sandbox with the same steps as live (KYB and the Service Agreement), then sign in and add test money on Fund wallet → Add test funds.
  2. Use the sandbox API key (Dashboard → API) with the sandbox base URL.
  3. Use the values below to get each outcome; anything else succeeds.
SendWhereResult
08000000000mobile_number (airtime, data)Declined by the network, refunded
201000000000smartcard / meter numberProcessing (202); delivered about 2 minutes later
300000000000smartcard / meter numberProvider times out; held as processing, then delivered
400000000000smartcard / meter numberFailed, refunded
0000000000smartcard, meter or account number lookupsNot found / not valid
2222222222beneficiaryAccountNumberTransfer pending (awaiting the bank)
1111111111beneficiaryAccountNumberTransfer refused, amount and fee refunded

To test deposits into your customers' account numbers, use Simulate a deposit: your wallet is credited and the signed webhook is sent, as for a real bank transfer.

Responses & errors

Check the HTTP status and the status field ("success"; Status repeats it as "successful"). Errors carry the reason in error (some endpoints use message). Keep the reference or transaction_id of every purchase: it is on the receipt in your dashboard.

HTTPMeaningWhat to do
200Done (or, for a transfer, status: "pending")Deliver to your customer.
202Paid and being processed by the provider (status: "pending")Do not retry. It is completed, or refunded to your wallet automatically, usually within minutes.
400Invalid request, insufficient balance, or declined/failed (already refunded)Show the error; fix and send again if needed.
401Missing or wrong API keyCheck the Authorization header.
404Not found (e.g. a user that is not yours)
422Validation error on the PIN endpoints (message, errors)Fix the fields listed.
429Too many requests: 60 per minute per key, or the same purchase repeated within secondsWait and retry.
500The provider or our service failed; the purchase is refundedRetry later.
Never send the same purchase twice because an answer was slow: use a long timeout (90 seconds) and, for transfers, your own unique reference.

Webhooks

For every deposit into one of your customers' account numbers (see Virtual accounts) we credit your wallet, less 1.2% (at most ₦350), and POST this JSON to that account's webhook_url:

{
    "virtualAccount": "9912345678",
    "amountReceived": "20000",
    "sessionId": "100004261017103000123456789012",
    "sourceAccountName": "ADA OBI",
    "reference": "pay_8c2f7a1d0b",
    "sourceBankName": "Guaranty Trust Bank",
    "status": "successful"
}

Verify every delivery before crediting anyone. x-api-signature is the HMAC-SHA512 of the raw body, keyed with your API key. For replay protection also check x-signature-v2: HMAC-SHA512 of "{x-webhook-timestamp}.{x-webhook-nonce}.{body}", and reject timestamps older than 5 minutes or a nonce you have seen.

Verify a webhook
$body = file_get_contents('php://input');
$expected = hash_hmac('sha512', $body, getenv('UNITYBILLS_API_KEY'));
if (!hash_equals($expected, $_SERVER['HTTP_X_API_SIGNATURE'] ?? '')) {
    http_response_code(401);
    exit;
}
$event = json_decode($body, true);
// Credit once per sessionId, then answer 200.
http_response_code(200);

Answer with a 2xx within 15 seconds. Otherwise we retry 4 more times (after 1, 5, 15 and 60 minutes). Process each sessionId only once.

Older URLs

These keep working for integrations built on them, with the same answers as before. New integrations should use the endpoints below.

URLSame as
/api/data, /api/wp-json/v1/api/data, /api/wp-json/v2/api/data, /api/wp-json/max/v1/api/data, /api/wp-json/max/v2/api/dataBuy data
/api/topupBuy airtime
GET /api/validatemeter, POST /api/billpayment (MSORG style: disconame/disco_name 1–11, MeterType 1 prepaid, 2 postpaid)Check a meter, Buy a token
/api/v1/fetch-data-pins (by reference_id), /api/v1/fetch-data-pins/topupmateTake data PINs
/api/v1/corporate/virtual-accountRetired: answers 400. Use Open an account number.

Account

GET /api/v1/user

Wallet balance and profile

Your profile and current wallet balance. Use it to check your key works and to show your balance.

Request
curl -X GET 'https://unitybills.ng/user' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "user": {
        "id": 1348,
        "email": "ada@adastores.ng",
        "FirstName": "Ada",
        "Phone": "08031234567",
        "Account_Balance": "50000.00000000",
        "wallet_balance": "50000.00"
    }
}

Airtime & data

Networks: 1 MTN, 2 GLO, 3 9MOBILE, 4 AIRTEL. A purchase that fails is refunded to your wallet at once; when the network declines it, reason says why. If the network does not answer in time you get 202 pending: do not retry; it is confirmed or refunded.

POST /api/v1/airtime

Buy airtime

Top up any Nigerian number. Merchants pay the discounted rate set on their account; the service discount is credited to the discount wallet.

Body fieldDescription
network integerrequired 1 MTN, 2 GLO, 3 9MOBILE, 4 AIRTEL.
amount numberrequired Naira, at least 50. Your tier sets the maximum.
mobile_number stringrequired The number to top up, e.g. 08031234567.
airtime_type stringrequired "VTU".
Ported_number booleanoptional true to skip the network/number prefix check (ported numbers). Default true.
Request
curl -X POST 'https://unitybills.ng/airtime' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"network":1,"amount":200,"mobile_number":"08031234567","airtime_type":"VTU","Ported_number":true}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Airtime purchase completed successfully",
    "transaction_details": {
        "status": "successful",
        "message": "Airtime purchase completed successfully",
        "transaction_id": "API-TRX75612411300594031B100D",
        "network": 1,
        "amount": 200,
        "phone_number": "08031234567"
    },
    "reference": "API-TRX75612411300594031B100D",
    "discount": null
}
GET /api/v1/data/plans

Data plans

The data plans on sale with the price you are charged. Filter by network and/or type, or send neither for every plan. `plan` is the id to buy with.

Query fieldDescription
network stringoptional 1 MTN, 2 GLO, 3 9MOBILE, 4 AIRTEL, or the name ("mtn"). Empty: all networks.
type stringoptional Plan type, e.g. SME, GIFTING, CORPORATE GIFTING (any case). Empty: all types.
Request
curl -X GET 'https://unitybills.ng/data/plans' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Data plans retrieved successfully",
    "data": [
        {
            "plan": 101,
            "network": 1,
            "network_name": "MTN",
            "type": "SME",
            "name": "1GB SME",
            "validity_days": 30,
            "amount": 260
        },
        {
            "plan": 102,
            "network": 1,
            "network_name": "MTN",
            "type": "SME",
            "name": "2GB SME",
            "validity_days": 7,
            "amount": 515
        },
        {
            "plan": 103,
            "network": 4,
            "network_name": "AIRTEL",
            "type": "GIFTING",
            "name": "Airtel 1.5GB Gifting",
            "validity_days": 30,
            "amount": 1020
        }
    ]
}
POST /api/v1/data

Buy data

Buy a data plan. Get plan ids and your prices from GET /data/plans; the plan decides the network and size.

Body fieldDescription
network integerrequired 1 MTN, 2 GLO, 3 9MOBILE, 4 AIRTEL (must match the plan).
plan integerrequired `plan` from GET /data/plans.
mobile_number stringrequired The number to credit.
Ported_number booleanoptional Default true.
Request
curl -X POST 'https://unitybills.ng/data' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"network":1,"plan":101,"mobile_number":"08031234567","Ported_number":true}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "true_response": "Data purchase successful",
    "transaction_details": {
        "id": 2384,
        "network": 1,
        "mobile_number": "08031234567",
        "Status": "successful",
        "api_response": "Dear Customer, You have successfully shared 1GB SME Data to 2348031234567.",
        "plan_network": "MTN",
        "plan_name": "1GB SME",
        "create_date": "2026-10-17T10:46:00+01:00",
        "Ported_number": true
    },
    "reference": "API-TRX85648114132853DB7981DE",
    "discount_applied": "none",
    "merchant_price_applied": "yes",
    "amount_debited": "260.00",
    "discount": null
}

Cable TV

DSTV, GOTV, Startimes and Showmax. Check the smartcard first, then pay. A bouquet change, Startimes and Showmax are charged at the plan price; a DSTV/GOTV renewal at the amount you send (the renewal amount).

GET /api/v1/cable/providers

Providers

The cable TV services you can pay.

Request
curl -X GET 'https://unitybills.ng/cable/providers' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Cable providers retrieved successfully",
    "data": [
        {
            "code": "dstv",
            "name": "DSTV",
            "image": "http://localhost/assets/images/brands/dstv.png"
        },
        {
            "code": "gotv",
            "name": "GOTV",
            "image": "http://localhost/assets/images/brands/gotv.png"
        },
        {
            "code": "startimes",
            "name": "Startimes",
            "image": "http://localhost/assets/images/brands/startimes.png"
        },
        {
            "code": "showmax",
            "name": "Showmax",
            "image": "http://localhost/assets/images/brands/showmax.png"
        }
    ]
}
GET /api/v1/cable/plans

Plans (bouquets)

Bouquets and current prices for a provider.

Query fieldDescription
service_id stringrequired dstv, gotv, startimes or showmax.
Request
curl -X GET 'https://unitybills.ng/cable/plans?service_id=gotv' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Cable plans retrieved successfully",
    "data": [
        {
            "variation_code": "gotv-smallie",
            "name": "GOtv Smallie N1,900",
            "amount": "1900.00",
            "fixedPrice": "Yes"
        },
        {
            "variation_code": "gotv-jinja",
            "name": "GOtv Jinja N3,900",
            "amount": "3900.00",
            "fixedPrice": "Yes"
        },
        {
            "variation_code": "gotv-jolli",
            "name": "GOtv Jolli N5,800",
            "amount": "5800.00",
            "fixedPrice": "Yes"
        },
        {
            "variation_code": "gotv-max",
            "name": "GOtv Max N8,500",
            "amount": "8500.00",
            "fixedPrice": "Yes"
        },
        {
            "variation_code": "gotv-supa",
            "name": "GOtv Supa N11,400",
            "amount": "11400.00",
            "fixedPrice": "Yes"
        }
    ]
}
POST /api/v1/cable/validate

Check a smartcard / IUC

The customer name, status and due date for a smartcard number.

Body fieldDescription
service_id stringrequired dstv, gotv, startimes or showmax.
smart_card_number stringrequired Smartcard / IUC number.
Request
curl -X POST 'https://unitybills.ng/cable/validate' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"service_id":"dstv","smart_card_number":"7032054653"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Customer validated successfully",
    "data": {
        "customer_name": "SANDBOX CUSTOMER",
        "customer_number": "032054653",
        "customer_type": "DSTV",
        "status": "ACTIVE",
        "due_date": "2026-10-29T00:00:00",
        "smart_card_number": "7032054653",
        "service_id": "dstv"
    }
}
POST /api/v1/cable/purchase

Pay a subscription

Renew the current bouquet or change to another.

Body fieldDescription
service_id stringrequired dstv, gotv, startimes or showmax.
smart_card_number stringrequired Smartcard / IUC (Showmax: phone number).
variation_code stringrequired Plan code from /cable/plans.
amount numberrequired Naira. At least the plan price for a change; the renewal amount for a renewal.
phone stringrequired Customer phone number.
subscription_type stringrequired "change" (new bouquet) or "renew".
Request
curl -X POST 'https://unitybills.ng/cable/purchase' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"service_id":"gotv","smart_card_number":"7032054653","variation_code":"gotv-max","amount":8500,"phone":"08031234567","subscription_type":"change"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Cable TV subscription purchased successfully",
    "data": {
        "transaction_id": "753389301442d19c75ee",
        "smart_card_number": "7032054653",
        "service_id": "gotv",
        "variation_code": "gotv-max",
        "subscription_type": "change",
        "amount": 8500,
        "balance_before": "49540.00000000",
        "balance_after": 41040
    }
}

Electricity

Prepaid tokens and postpaid bills for every distribution company. Check the meter first, then pay.

GET /api/v1/utility/providers

Distribution companies

The service_id of each DisCo.

Request
curl -X GET 'https://unitybills.ng/utility/providers' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Utility providers retrieved successfully",
    "data": [
        {
            "code": "ikeja-electric",
            "name": "Ikeja Electric Payment - IKEDC",
            "type": "electricity"
        },
        {
            "code": "eko-electric",
            "name": "Eko Electricity - EKEDC",
            "type": "electricity"
        },
        {
            "code": "kano-electric",
            "name": "Kano Electricity - KEDCO",
            "type": "electricity"
        },
        {
            "code": "portharcourt-electric",
            "name": "Port Harcourt Electricity - PHED",
            "type": "electricity"
        },
        {
            "code": "jos-electric",
            "name": "Jos Electricity - JED",
            "type": "electricity"
        },
        {
            "code": "ibadan-electric",
            "name": "Ibadan Electricity - IBEDC",
            "type": "electricity"
        },
        {
            "code": "kaduna-electric",
            "name": "Kaduna Electricity - KAEDCO",
            "type": "electricity"
        },
        {
            "code": "abuja-electric",
            "name": "Abuja Electricity - AEDC",
            "type": "electricity"
        }
    ]
}
POST /api/v1/utility/validate

Check a meter

The customer name and address on a meter.

Body fieldDescription
service_id stringrequired e.g. ikeja-electric, kano-electric.
meter_number stringrequired Meter number.
meter_type stringrequired "prepaid" or "postpaid".
Request
curl -X POST 'https://unitybills.ng/utility/validate' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"service_id":"kano-electric","meter_number":"45012345678","meter_type":"prepaid"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Meter validated successfully",
    "data": {
        "customer_name": "SANDBOX CUSTOMER",
        "meter_number": "45012345678",
        "meter_type": "prepaid",
        "address": "12 Sandbox Street, Kano"
    }
}
POST /api/v1/utility/purchase

Buy a token / pay a bill

Prepaid returns the token; postpaid pays the bill.

Body fieldDescription
service_id stringrequired From /utility/providers.
meter_number stringrequired Meter number.
meter_type stringrequired "prepaid" or "postpaid".
amount numberrequired Naira, at least 100 (the DisCo may require more).
phone stringrequired Customer phone number.
Request
curl -X POST 'https://unitybills.ng/utility/purchase' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"service_id":"kano-electric","meter_number":"45012345678","meter_type":"prepaid","amount":5000,"phone":"08031234567"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Utility payment successful",
    "data": {
        "transaction_id": "551731115013276220d7",
        "meter_number": "45012345678",
        "amount": 5000,
        "units": "74.8 kWh",
        "token": "Token : 1005-0253-8254-0262-2022",
        "purchased_code": "Token : 1005-0253-8254-0262-2022",
        "balance_before": "32540.00000000",
        "balance_after": 27540
    }
}

Exam PINs

WAEC result checker and registration PINs, and JAMB (UTME / Direct Entry) PINs.

GET /api/v1/education/services

Services

The exam PIN services.

Request
curl -X GET 'https://unitybills.ng/education/services' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Education services retrieved successfully",
    "data": [
        {
            "service_id": "waec",
            "name": "WAEC Result Checker",
            "description": "West African Examinations Council Result Checker",
            "type": "result_checker",
            "image": "http://localhost/assets/images/brands/waec.png"
        },
        {
            "service_id": "neco",
            "name": "NECO Result Checker",
            "description": "National Examinations Council Result Checker",
            "type": "result_checker",
            "image": "http://localhost/assets/images/brands/neco.png"
        },
        {
            "service_id": "jamb",
            "name": "JAMB PIN",
            "description": "Joint Admissions and Matriculation Board PIN",
            "type": "pin_vending",
            "image": "http://localhost/assets/images/brands/jamb.png"
        },
        {
            "service_id": "waec-registration",
            "name": "WAEC Registration",
            "description": "WAEC Registration PIN for Private Candidates",
            "type": "registration",
            "image": "http://localhost/assets/images/brands/waec.png"
        }
    ]
}
GET /api/v1/education/plans

Plans and prices

Variations and prices of a service.

Query fieldDescription
service_id stringrequired waec, waec-registration or jamb.
Request
curl -X GET 'https://unitybills.ng/education/plans?service_id=jamb' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Education variations retrieved successfully",
    "data": {
        "service_name": "Jamb PIN VENDING (UTME & Direct Entry)",
        "service_id": "jamb",
        "variations": [
            {
                "variation_code": "utme-mock",
                "name": "UTME PIN (with mock)",
                "amount": "8700.00",
                "fixedPrice": "Yes"
            },
            {
                "variation_code": "utme-no-mock",
                "name": "UTME PIN (without mock)",
                "amount": "7200.00",
                "fixedPrice": "Yes"
            },
            {
                "variation_code": "de",
                "name": "Direct Entry (DE)",
                "amount": "7200.00",
                "fixedPrice": "Yes"
            }
        ]
    }
}
POST /api/v1/education/validate/profile

Check a JAMB profile

The candidate name on a JAMB profile id.

Body fieldDescription
service_id stringrequired "jamb".
profile_id stringrequired The candidate's JAMB profile id.
variation_code stringrequired utme-mock, utme-no-mock or de.
Request
curl -X POST 'https://unitybills.ng/education/validate/profile' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"service_id":"jamb","profile_id":"0123456789","variation_code":"utme-no-mock"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Profile validated successfully",
    "data": {
        "customer_name": "SANDBOX CANDIDATE",
        "profile_id": "0123456789",
        "service_id": "jamb"
    }
}
POST /api/v1/education/purchase

Buy exam PINs

Up to 10 PINs per request. The PINs are in the answer.

Body fieldDescription
service_id stringrequired waec, waec-registration or jamb.
variation_code stringrequired From /education/plans.
amount numberrequired Price of one PIN (at least the plan price).
phone stringrequired Customer phone number.
quantity integeroptional 1 to 10. Default 1.
profile_id stringoptional Required for JAMB.
Request
curl -X POST 'https://unitybills.ng/education/purchase' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"service_id":"waec","variation_code":"waecdirect","amount":3900,"phone":"08031234567","quantity":2}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "Education service purchased successfully",
    "data": {
        "transaction_id": "905034103252ac997ae9",
        "service_id": "waec",
        "variation_code": "waecdirect",
        "quantity": 2,
        "amount": 7800,
        "purchased_code": "Serial No:WRN442111456, pin: 403252233181||Serial No:WRN137117856, pin: 305020344716",
        "balance_before": "22540.00000000",
        "balance_after": 14740,
        "cards": [
            {
                "Serial": "WRN442111456",
                "Pin": "403252233181"
            },
            {
                "Serial": "WRN137117856",
                "Pin": "305020344716"
            }
        ]
    }
}

Bank transfers

Send money from your wallet to any Nigerian bank account. Each transfer costs a flat fee, debited with the amount. The fee is ₦50 per transfer.

GET /api/v1/banks

Bank list

Bank codes to use for transfers and account lookups.

Request
curl -X GET 'https://unitybills.ng/banks' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json'
Response · 200 OK
{
    "status": "success",
    "data": [
        {
            "code": "044",
            "name": "Access Bank"
        },
        {
            "code": "023",
            "name": "Citibank Nigeria"
        },
        {
            "code": "050",
            "name": "Ecobank Nigeria"
        },
        {
            "code": "070",
            "name": "Fidelity Bank"
        },
        {
            "code": "011",
            "name": "First Bank of Nigeria"
        },
        {
            "code": "214",
            "name": "First City Monument Bank"
        },
        {
            "code": "058",
            "name": "Guaranty Trust Bank"
        },
        {
            "code": "030",
            "name": "Heritage Bank"
        },
        {
            "code": "301",
            "name": "Jaiz Bank"
        },
        {
            "code": "082",
            "name": "Keystone Bank"
        },
        {
            "code": "50211",
            "name": "Kuda Microfinance Bank"
        },
        {
            "code": "50515",
            "name": "Moniepoint Microfinance Bank"
        },
        {
            "code": "999992",
            "name": "OPay"
        },
        {
            "code": "999991",
            "name": "PalmPay"
        },
        {
            "code": "076",
            "name": "Polaris Bank"
        },
        {
            "code": "221",
            "name": "Stanbic IBTC Bank"
        },
        {
            "code": "068",
            "name": "Standard Chartered Bank"
        },
        {
            "code": "232",
            "name": "Sterling Bank"
        },
        {
            "code": "032",
            "name": "Union Bank of Nigeria"
        },
        {
            "code": "033",
            "name": "United Bank For Africa"
        },
        {
            "code": "215",
            "name": "Unity Bank"
        },
        {
            "code": "035",
            "name": "Wema Bank"
        },
        {
            "code": "057",
            "name": "Zenith Bank"
        }
    ]
}
POST /api/v1/verify-account

Look up an account name

Confirm who owns an account before you send money.

Body fieldDescription
bank_code stringrequired From /banks.
account_number stringrequired 10 digits.
Request
curl -X POST 'https://unitybills.ng/verify-account' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"bank_code":"058","account_number":"0123456789"}'
Response · 200 OK
{
    "status": "success",
    "account_name": "SANDBOX ACCOUNT 6789"
}
POST /api/v1/transfer

Send a transfer

Use your own unique reference so you can match the result. "pending" means the bank has not confirmed yet; if it then fails, the amount and fee are refunded automatically.

Body fieldDescription
beneficiaryBankCode stringrequired From /banks.
beneficiaryAccountNumber stringrequired 10 digits.
amount numberrequired Naira.
narration stringoptional Shown to the recipient (up to 100 characters).
reference stringoptional Your unique reference.
senderName stringoptional Name shown as the sender.
Request
curl -X POST 'https://unitybills.ng/transfer' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"beneficiaryBankCode":"058","beneficiaryAccountNumber":"0123456789","amount":1000,"narration":"Payout","reference":"PAYOUT-0001"}'
Response · 200 OK
{
    "success": true,
    "message": "Transfer successful",
    "data": {
        "reference": "PAYOUT-0001",
        "status": "successful",
        "amount": 1000,
        "currency": "NGN",
        "created_at": null,
        "completed_at": null,
        "beneficiary": {
            "account_number": "0123456789",
            "account_name": "SANDBOX ACCOUNT 6789",
            "bank_name": "Guaranty Trust Bank",
            "bank_code": "058"
        },
        "narration": "Payout",
        "provider_message": "Approved"
    }
}

Virtual accounts

Give each customer their own account number. Money sent to it is credited to your wallet (less the deposit fee) and reported to the account's webhook_url. Needs an approved business verification (KYB).

POST /api/v1/virtual-account

Open an account number

Calling again for the same customer returns the same account.

Body fieldDescription
firstname stringrequired Customer first name.
lastname stringrequired Customer last name.
phoneNumber stringrequired Customer phone number.
webhook_url stringrequired https URL that receives this account's deposits.
bvn stringoptional 11 digits. bvn or nin is required.
nin stringoptional 11 digits. bvn or nin is required.
email stringoptional Customer email.
Request
curl -X POST 'https://unitybills.ng/virtual-account' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"firstname":"Ada","lastname":"Obi","phoneNumber":"08031234567","nin":"12345678901","webhook_url":"https://yourapp.ng/hooks/deposits"}'
Response · 200 OK
{
    "success": true,
    "data": {
        "metadata": [],
        "createdAt": 1792233720000,
        "updatedAt": 1792233720000,
        "id": 4629,
        "accountNumber": "3452858578",
        "accountName": "UNITYBILLS/ADA OBI (ADA STORES)",
        "accountType": "individual",
        "firstname": "Ada Stores",
        "lastname": "Ada",
        "middlename": "Obi",
        "mobileNumber": "08031234567",
        "externalReference": "ub020no557871337m979o0n085",
        "emailAddress": "ada@adastores.ng",
        "bvn": "12345678901",
        "gender": "male",
        "address": "",
        "dateOfBirth": "",
        "validityType": "permanent"
    }
}

Data & airtime PINs

Hand out PINs you generated (and paid for) on the dashboard, e.g. to print or sell in your own app. Each PIN is given out once.

POST /api/v1/fetch-data-pins/v2

Take data PINs

Customers load a PIN by dialling *7006*47*PIN#.

Body fieldDescription
network stringrequired Network id, e.g. "1".
data_plan stringrequired Your USSD app plan id (Dashboard → Data PINs).
quantity integerrequired How many PINs.
Request
curl -X POST 'https://unitybills.ng/fetch-data-pins/v2' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"network":"1","data_plan":"31","quantity":2}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "quantity": "2",
    "pin": "0029224,1743805",
    "serial": "B-2283-7455,B-2283-7455",
    "load_pin": "*7006*47*pin#",
    "check_balance": "*323*4#"
}
POST /api/v1/fetch-recharge-pins

Take airtime PINs

Airtime recharge PINs from your stock.

Body fieldDescription
network stringrequired Network id, e.g. "1".
plan stringrequired Plan code of the PINs (Dashboard → Recharge PINs).
quantity integerrequired How many PINs.
Request
curl -X POST 'https://unitybills.ng/fetch-recharge-pins' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"network":"1","plan":"MTN100","quantity":2}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "quantity": "2",
    "pin": "51340860747,17534456595",
    "serial": "SN305512,SN818276",
    "load_pin": "*7006*47*pin#",
    "check_balance": "*310#"
}

USSD app users

Manage the subscribers of your USSD app: their wallets, status and transactions.

POST /api/v1/ussd/users/list

List users

Paginated, newest first.

Body fieldDescription
search stringoptional Name or phone.
status stringoptional active or inactive.
app_id integeroptional One of your apps.
page integeroptional Default 1.
per_page integeroptional 1 to 100.
Request
curl -X POST 'https://unitybills.ng/ussd/users/list' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"status":"active"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "App users retrieved successfully",
    "data": {
        "users": [
            {
                "id": 501,
                "fullname": "Chidi Eze",
                "phone": "2348031112222",
                "balance": "1,500.00",
                "status": "active",
                "account_number": "1823035203",
                "account_name": "Chidi Eze",
                "bank_name": "Wema Bank",
                "app": {
                    "id": 12,
                    "name": "Ada Data"
                },
                "created_at": "2026-09-17 10:30:00",
                "updated_at": "2026-09-17 10:30:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "per_page": 15,
            "total": 1,
            "last_page": 1,
            "has_more_pages": false
        },
        "summary": {
            "total_users": 1,
            "active_users": 1,
            "total_balance": "1,500.00"
        }
    }
}
POST /api/v1/ussd/users/create

Register a user

Creates the user and their wallet; with bvn or nin, also an account number for funding.

Body fieldDescription
fullname stringrequired Full name.
phone stringrequired Nigerian phone number.
pin stringrequired 4 to 6 digits.
app_id integerrequired Your app id.
bvn stringoptional 11 digits.
nin stringoptional 11 digits.
Request
curl -X POST 'https://unitybills.ng/ussd/users/create' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"fullname":"Musa Bello","phone":"08035555555","pin":"1234","app_id":12,"nin":"12345678901"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "App user created successfully",
    "data": {
        "user": {
            "id": 1851,
            "fullname": "Musa Bello",
            "phone": "2348035555555",
            "balance": "0.00",
            "status": "active",
            "app_id": 12,
            "created_at": "2026-10-17 11:52:00"
        },
        "virtual_account": {
            "success": true,
            "account_number": "2349826071",
            "account_name": "UNITYBILLS/MUSA BELLO",
            "bank_name": "Wema Bank",
            "message": ""
        }
    }
}
POST /api/v1/ussd/users/details

One user

Profile and wallet.

Body fieldDescription
user_id integerrequired The user id.
Request
curl -X POST 'https://unitybills.ng/ussd/users/details' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"user_id":501}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "App user details retrieved successfully",
    "data": {
        "user": {
            "id": 501,
            "fullname": "Chidi Eze",
            "phone": "2348031112222",
            "balance": "1,500.00",
            "status": "active",
            "account_number": "1823035203",
            "account_name": "Chidi Eze",
            "bank_name": "Wema Bank",
            "app": {
                "id": 12,
                "name": "Ada Data"
            },
            "created_at": "2026-09-17 10:30:00",
            "updated_at": "2026-09-17 10:30:00"
        },
        "recent_transactions": [
            {
                "id": 1559,
                "transaction_id": "TXN-66065",
                "amount": "280.00",
                "type": "buy_data",
                "description": "1GB SME data",
                "status": "successful",
                "created_at": "2026-10-15 10:30:00"
            }
        ]
    }
}
POST /api/v1/ussd/users/update-balance

Credit or debit a wallet

A debit cannot take the wallet below zero.

Body fieldDescription
user_id integerrequired The user id.
action_type stringrequired credit or debit.
amount numberrequired Naira.
note stringoptional Your note.
Request
curl -X POST 'https://unitybills.ng/ussd/users/update-balance' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"user_id":501,"action_type":"credit","amount":500,"note":"Cash top-up"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "User successfully credited with ₦500.00",
    "data": {
        "user_id": 501,
        "action_type": "credit",
        "amount": "500.00",
        "balance_before": "1,500.00",
        "balance_after": "2,000.00",
        "transaction_id": "N170O40963M",
        "performed_at": "2026-10-17 11:56:00"
    }
}
POST /api/v1/ussd/users/update-status

Activate or suspend

A suspended user cannot use the app.

Body fieldDescription
user_id integerrequired The user id.
status stringrequired active or inactive.
Request
curl -X POST 'https://unitybills.ng/ussd/users/update-status' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"user_id":501,"status":"inactive"}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "User status updated successfully from active to inactive",
    "data": {
        "user_id": 501,
        "old_status": "active",
        "new_status": "inactive",
        "updated_at": "2026-10-17 11:58:00"
    }
}
POST /api/v1/ussd/users/transactions

A user's transactions

Paginated, newest first.

Body fieldDescription
user_id integerrequired The user id.
type stringoptional e.g. buy_data.
status stringoptional successful, pending or failed.
from_date dateoptional YYYY-MM-DD.
to_date dateoptional YYYY-MM-DD.
page integeroptional Default 1.
per_page integeroptional 1 to 100.
Request
curl -X POST 'https://unitybills.ng/ussd/users/transactions' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"user_id":501}'
Response · 200 OK
{
    "status": "success",
    "Status": "successful",
    "message": "User transactions retrieved successfully",
    "data": {
        "user": {
            "id": 501,
            "fullname": "Chidi Eze",
            "phone": "2348031112222",
            "current_balance": "2,000.00"
        },
        "transactions": [
            {
                "id": 8280,
                "transaction_id": "N170O40963M",
                "amount": "500.00",
                "type": "fund_wallet",
                "description": "Cash top-up",
                "status": "successful",
                "recipient": null,
                "created_at": "2026-10-17 11:56:00"
            },
            {
                "id": 8522,
                "transaction_id": "TXN-66065",
                "amount": "280.00",
                "type": "buy_data",
                "description": "1GB SME data",
                "status": "successful",
                "recipient": "08031112222",
                "created_at": "2026-10-15 10:30:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "per_page": 20,
            "total": 2,
            "last_page": 1,
            "has_more_pages": false
        }
    }
}

Sandbox tools

Available on the sandbox only.

POST /api/v1/sandbox/deposit

Simulate a deposit

Pays money into one of your virtual accounts as a bank transfer would: your wallet is credited and the signed deposit webhook is sent to the account's webhook_url.

Body fieldDescription
account_number stringrequired One of your virtual account numbers.
amount numberrequired Naira, 100 to 10,000,000.
sender_name stringoptional Default SANDBOX SENDER.
sender_bank stringoptional Default Guaranty Trust Bank.
Request
curl -X POST 'https://unitybills.ng/sandbox/deposit' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"account_number":"9912345678","amount":20000,"sender_name":"ADA OBI"}'
Response · 200 OK
{
    "status": "success",
    "message": "Webhook processed and email sent",
    "data": {
        "event": "collection",
        "reference": "UNITYBILLSADAOBI_04331037299253325e3b4",
        "virtualAccount": "9912345678",
        "amountReceived": 20000,
        "sessionId": "SBX6530269555321729EAAF",
        "sourceCurrency": "NGN",
        "destinationCurrency": "NGN",
        "status": "successful",
        "sourceAccountNumber": "",
        "sourceAccountName": "ADA OBI",
        "sourceBankCode": "",
        "sourceBankName": "Guaranty Trust Bank",
        "remarks": "FRM ADA OBI|Transfer from ADA OBI",
        "createdAt": 1792234920000,
        "updatedAt": 1792234920000,
        "settlementDestination": "9912345678"
    }
}

Help

Questions about the API, higher limits or going live? Contact us or email support@unitybills.ng. Include the reference of the request you are asking about.