Baton Developer API

Sell bills from your prepaid Baton wallet over a simple REST API. All amounts are in kobo (1 NGN = 100 kobo). Airtime, data bundles, electricity (prepaid and postpaid) and cable TV are live today.

1. How it works

  1. Create a business account and wait for Baton to approve it.
  2. Fund your wallet by transferring to your dedicated account number.
  3. Create an API key in your dashboard.
  4. Call the transactions endpoint. Each sale debits your wallet instantly.
  5. Receive a webhook when the sale settles, or poll the transaction.

2. Funding your wallet

Your dashboard shows an account number that belongs only to your business. Any transfer into it credits your wallet automatically, usually within seconds, and appears in your wallet history. A sale is declined with 402 when the balance is short.

3. Authentication

Pass your key id and secret as a bearer token, separated by a colon.

Authorization: Bearer pk_xxx:sk_yyy

Requests are rate limited per key (120 per minute by default). On overflow you get 429 with Retry-After and X-RateLimit-* headers.

4. Base URL

https://batonbills.com/api/public/partners/v1

5. List products

curl https://batonbills.com/api/public/partners/v1/products \
  -H "Authorization: Bearer pk_xxx:sk_yyy"
{
  "products": [
    { "slug": "mtn-airtime", "name": "MTN Airtime", "pricing_mode": "variable", "category": "airtime", "price_kobo": null, "discount_bps": 200, "your_price_kobo": null }
  ]
}

Only categories enabled for API sales are returned. Today that is airtime, data bundles, electricity and cable TV. Data bundles are fixed-price: send the exact price_kobo as amount_kobo and your wallet is charged your_price_kobo.
discount_bps is your reseller discount in basis points (200 = 2%). For variable-amount products your wallet is charged the amount you send less that discount, so a NGN 1,000 MTN top-up at 2% costs you NGN 980. Current airtime discounts: MTN 2%, Airtel 2%, Glo 4%, 9mobile 4%. Data bundles carry the same discount per network.

6. Check your wallet

curl https://batonbills.com/api/public/partners/v1/balance \
  -H "Authorization: Bearer pk_xxx:sk_yyy"

7. Sell airtime

Send an Idempotency-Key unique to your order. Replaying the same key with the same body returns the original sale; a different body returns 409.

curl -X POST https://batonbills.com/api/public/partners/v1/transactions \
  -H "Authorization: Bearer pk_xxx:sk_yyy" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-12345" \
  -d '{
    "product_slug": "mtn-airtime",
    "recipient": "08012345678",
    "amount_kobo": 50000,
    "reference": "order-12345"
  }'
{
  "transaction": {
    "reference": "BTN-...",
    "partner_reference": "order-12345",
    "status": "success",
    "recipient": "08012345678",
    "amount_kobo": 50000,
    "amount_charged_kobo": 49000,
    "discount_kobo": 1000
  }
}

amount_kobo is the airtime value delivered. amount_charged_kobo is what left your wallet after your discount.

7a. Sell data bundles

Each bundle is its own fixed-price product, for example data-mtn-200. Call /products and filter by category data for the full list across MTN, Airtel, Glo and 9mobile, with prices. Send the exact price_kobo as amount_kobo and the phone number as recipient. Your wallet is charged your_price_kobo (the price less your per-network discount, same as airtime).

curl -X POST https://batonbills.com/api/public/partners/v1/transactions \
  -H "Authorization: Bearer pk_xxx:sk_yyy" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-456" \
  -d '{
    "product_slug": "data-mtn-200",
    "recipient": "08012345678",
    "amount_kobo": 10000,
    "reference": "order-456"
  }'
{
  "transaction": {
    "reference": "BTN-...",
    "status": "success",
    "recipient": "08012345678",
    "amount_kobo": 10000,
    "amount_charged_kobo": 9800,
    "discount_kobo": 200
  }
}

Sending an amount that does not match the bundle price returns 400.

7b. Sell electricity (prepaid and postpaid)

All major discos are available, prepaid and postpaid: Abuja, Eko, Ikeja, Ibadan, Enugu, Port Harcourt, Benin, Jos, Kaduna, Kano, Yola, Aba Power, Access Power, Bonny Utility and Brains & Hammers. Slugs follow the pattern ekedc-prepaid, ikeja-postpaid, phed-prepaid (Ikeja, Eko and Abuja prepaid are ikedc-prepaid, ekedc-prepaid, aedc-prepaid); call /products for the full list. Always verify the meter first so you can show your customer the name on the meter before taking payment. Lookups are free.

curl -X POST https://batonbills.com/api/public/partners/v1/lookup \
  -H "Authorization: Bearer pk_xxx:sk_yyy" \
  -H "Content-Type: application/json" \
  -d '{ "product_slug": "ekedc-prepaid", "recipient": "62120126537" }'
{
  "valid": true,
  "recipient": "62120126537",
  "customer_name": "JOHN DOE",
  "address": "1B Sample Close, Lagos",
  "minimum_amount_kobo": 100000
}

An unknown meter returns { "valid": false, "message": "..." }. Then sell with the same transactions endpoint. The amount is in whole naira (multiple of 100 kobo), minimum NGN 1,000. Required: pass the customer's 11-digit mobile number as metadata.phone (the token is delivered to it). The meter number goes in recipient. Missing or invalid phone returns 400 before your wallet is charged.

curl -X POST https://batonbills.com/api/public/partners/v1/transactions \
  -H "Authorization: Bearer pk_xxx:sk_yyy" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-778" \
  -d '{
    "product_slug": "ekedc-prepaid",
    "recipient": "62120126537",
    "amount_kobo": 1000000,
    "reference": "order-778",
    "metadata": { "phone": "08012345678" }
  }'
{
  "transaction": {
    "reference": "BTN-...",
    "status": "success",
    "recipient": "62120126537",
    "amount_kobo": 1000000,
    "amount_charged_kobo": 1000000,
    "token": "4095-6299-9589-6790-1502",
    "token_units": "44.4"
  }
}

Give the token to your customer to load on the meter. If the disco is slow you get 202 with status pending; the token arrives in the transaction.success webhook, or poll the transaction. Failed sales are refunded to your wallet automatically.

7c. Sell cable TV (DStv, GOtv, StarTimes)

Each bouquet is its own fixed-price product, for example dstv-compe36 (DStv Compact), gotv-gotvmax (GOtv Max) or startimes-basic. Call /products for the full list with prices. Verify the smartcard first:

curl -X POST https://batonbills.com/api/public/partners/v1/lookup \
  -H "Authorization: Bearer pk_xxx:sk_yyy" \
  -H "Content-Type: application/json" \
  -d '{ "product_slug": "dstv-compe36", "recipient": "8213124636" }'
{
  "valid": true,
  "recipient": "8213124636",
  "customer_name": "JOHN DOE",
  "current_bouquet": "DStv Compact",
  "due_date": "2026-10-04T00:00:00"
}

Then sell with the transactions endpoint, sending the exact price_kobo as amount_kobo, and the smartcard or IUC number as recipient. The customer's 11-digit mobile number in metadata.phone is required.

curl -X POST https://batonbills.com/api/public/partners/v1/transactions \
  -H "Authorization: Bearer pk_xxx:sk_yyy" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-901" \
  -d '{
    "product_slug": "dstv-compe36",
    "recipient": "8213124636",
    "amount_kobo": 1900000,
    "reference": "order-901",
    "metadata": { "phone": "08012345678" }
  }'

8. Fetch one transaction

curl https://batonbills.com/api/public/partners/v1/transactions/BTN-XXXX \
  -H "Authorization: Bearer pk_xxx:sk_yyy"

9. Webhooks

Register an https endpoint in your dashboard. Each delivery is signed with your webhook secret; verify the signature header before trusting the payload. Failed deliveries can be replayed from the dashboard.

10. Errors

  • 400 validation problem or unknown product
  • 401 missing or invalid key
  • 402 not enough wallet balance
  • 403 key missing a scope, business not approved, or category not enabled
  • 409 idempotency key reused with a different body
  • 429 rate limited

Ready to start?

Create your business account, fund the wallet and generate a key.

Create a business account