Bill payments

With the Flutterwave API, you can pay for several kinds of bills (including airtime, data, electricity and cable TV) from your Flutterwave wallet.

You earn a 3% commission for each successful airtime purchase, and a 30 naira commission on other bill types.

When paying bills on Flutterwave, you'll make a POST request to the create a bill payment endpoint. You'll need to specify:

  • type: The type of the bill. Here's how to get this value.
  • country: The two-letter code for the country the service is in. For example, if you're buying airtime for a Nigerian phone number, country will be "NG".
  • customer: An identifier for the customer, such as their phone number or decoder number. Here's how to get this value.
  • amount: The amount to be paid for the bill. The amount will be deducted from your Flutterwave available balance.
  • reference: An optional reference code to identify the transaction. It should be unique for each transaction.

If you're buying airtime in Ghana, you'll also need to specify the biller_name, which is returned from the get bill categories endpoint.

The type, customer, and customer are quite important, so let's discuss them in a bit more detail.

Getting the type and amount values

Bills can be of different types, such as airtime, tv, or data bundles. To use the endpoint, you'll need to supply a type field that specifies the type of bill you're paying.

To get the type and amount values, you'll need to call the get bill categories endpoint. The endpoint will return a list of all supported bill categories, so you can limit the results by passing the appropriate query parameter. For example:

  • To get airtime only, use airtime=1.
  • To get mobile data bundles only (MTN, Glo etc), use data_bundle=1.
  • To get Wi-Fi Internet bundles only (Smile, Spectranet etc), use internet=1.
  • To get electricity bills only (IKEDC, EKEDC etc), use power=1.
  • To get cable TV subscriptions only (DStv, GoTV, etc), use cables=1.
  • To get tolls only, use toll=1.

Here are a few examples:

// https://api.flutterwave.com/v3/bill-categories?airtime=1
{
  "status": "success",
  "message": "bill categories retrieval successful",
  "data": [
    {
      "id": 1,
      "biller_code": "BIL099",
      "name": "AIRTIME",
      "default_commission": 0.03,
      "date_added": "2020-09-17T15:56:40.,22Z"
      "country": "NG",
      "is_airtime": true,
      "biller_name": "AIRTIME",
      "item_code": "AT099",
      "short_name": "MTN NIGERIA",
      "fee": 0,
      "commission_on_fee": false,
      "label_name": "Mobile Number",
      "amount": 0
    },
    {
      "id": 2,
      "biller_code": "BIL100",
      "name": "AIRTEL NIGERIA",
      "default_commission": 0.03,
      "date_added": "2020-09-17T15:56:41.,257Z"
      "country": "NG",
      "is_airtime": true,
      "biller_name": "AIRTEL VTU",
      "item_code": "AT100",
      "short_name": "AIRTEL NIGERIA",
      "fee": 0,
      "commission_on_fee": false,
      "label_name": "Mobile Number",
      "amount": 0
    }
  ]
}
// https://api.flutterwave.com/v3/bill-categories?data_bundle=1
{
  "status": "success",
  "message": "bill categories retrieval successful",
  "data": [
    {
      "id": 365,
      "biller_code": "BIL108",
      "name": "MTN 200 MB DATA BUNDLE",
      "default_commission": 0.03,
      "date_added": "2020-02-11T11:16:42.727Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "MTN 200 MB DATA BUNDLE",
      "item_code": "MD142",
      "short_name": "MTN 200 MB DATA BUNDLE",
      "fee": 0,
      "commission_on_fee": false,
      "label_name": "Mobile Number",
      "amount": 200
    },
    {
      "id": 374,
      "biller_code": "BIL109",
      "name": "GLO 35 MB data bundle",
      "default_commission": 0.03,
      "date_added": "2020-02-11T11:16:42.727Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "GLO 35 MB data bundle",
      "item_code": "MD146",
      "short_name": "GLO 35 MB data bundle",
      "fee": 0,
      "commission_on_fee": false,
      "label_name": "Mobile Number",
      "amount": 100
    }
  ]
}
// https://api.flutterwave.com/v3/bill-categories?internet=1
{
  "status": "success",
  "message": "bill categories retrieval successful",
  "data": [
    {
      "id": 39,
      "biller_code": "BIL101",
      "name": "SMILE",
      "default_commission": 0.1,
      "date_added": "2020-09-17T15:57:04.,277Z"
      "country": "NG",
      "is_airtime": false,
      "biller_name": "Smile 1GB Data, Bundle"
      "item_code": "IS101",
      "short_name": "SMILE",
      "fee": 100,
      "commission_on_fee": true,
      "label_name": "Account Number",
      "amount": 100
    },
    {
      "id": 40,
      "biller_code": "BIL121",
      "name": "SWIFT",
      "default_commission": 0.1,
      "date_added": "2020-09-17T15:57:05.,237Z"
      "country": "NG",
      "is_airtime": false,
      "biller_name": "Swift Subscription",
      "item_code": "IS142",
      "short_name": "SWIFT",
      "fee": 100,
      "commission_on_fee": true,
      "label_name": "Account Number",
      "amount": 0
    }
  ]
}
// https://api.flutterwave.com/v3/bill-categories?power=1
{
  "status": "success",
  "message": "bill categories retrieval successful",
  "data": [
    {
      "id": 269,
      "biller_code": "BIL113",
      "name": "IKEDC Prepaid topup",
      "default_commission": 0.3,
      "date_added": "2020-02-11T11:09:48.087Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "IKEDC PREPAID TOP UP",
      "item_code": "UB159",
      "short_name": "IKEDC PREPAID TOP UP",
      "fee": 100,
      "commission_on_fee": true,
      "label_name": "Meter Number",
      "amount": 0
    },
    {
      "id": 267,
      "biller_code": "BIL112",
      "name": "EKO DISCO ELECTRICITY BILLS FOR PREPAID CLIENTS",
      "default_commission": 0.3,
      "date_added": "2020-02-11T11:09:48.087Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "EKEDC PREPAID TOPUP",
      "item_code": "UB157",
      "short_name": "EKEDC PREPAID TOPUP",
      "fee": 100,
      "commission_on_fee": true,
      "label_name": "Meter Number",
      "amount": 0
    }
  ]
}
// https://api.flutterwave.com/v3/bill-categories?cables=1
{
  "status": "success",
  "message": "bill categories retrieval successful",
  "data": [
    {
      "id": 34,
      "biller_code": "BIL119",
      "name": "DSTV Payment",
      "default_commission": 0.1,
      "date_added": "2020-09-17T15:56:58.057Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "DSTV Payment",
      "item_code": "CB140",
      "short_name": "DSTV",
      "fee": 100,
      "commission_on_fee": true,
      "label_name": "SmartCard Number",
      "amount": 1900
    }
    {
      "id": 35,
      "biller_code": "BIL119",
      "name": "DSTV",
      "default_commission": 0.1,
      "date_added": "2020-09-17T15:56:58.057Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "DSTV Access",
      "item_code": "CB141",
      "short_name": "DSTV",
      "fee": 100,
      "commission_on_fee": true,
      "label_name": "SmartCard Number",
      "amount": 1800
    }
  ]
}

The biller_name field in the response is what you'll use as the type when creating a bill payment.

Take note of the amount field in the bill category you want to pay. If it's 0 (for example, in airtime), then you can send any amount you want when purchasing that bill. If it's not 0 (for example, in data bundles), then the amount you send must match the amount specified.

Using biller codes

You can also filter the list of bill categories by biller. To get all bills provided by a specific biller, pass biller_code={{BILLER_CODE}}. For example, all MTN data bundles have the biller_code as BIL104, so adding the query parameterbiller_code=BIL104 will retrieve only MTN data bundles.

{
  "status": "success",
  "message": "bill categories retrieval successful",
  "data": [
    {
      "id": 5,
      "biller_code": "BIL104",
      "name": "MTN DATA BUNDLE",
      "default_commission": 0.1,
      "date_added": "2020-09-17T15:56:44.653Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "MTN 50 MB",
      "item_code": "MD104",
      "short_name": "MTN DATA BUNDLE",
      "fee": 0,
      "commission_on_fee": false,
      "label_name": "Mobile Number",
      "amount": 100
    }
    {
      "id": 6,
      "biller_code": "BIL104",
      "name": "MTN DATA BUNDLE",
      "default_commission": 0.1,
      "date_added": "2020-09-17T15:56:44.653Z",
      "country": "NG",
      "is_airtime": false,
      "biller_name": "MTN 150 MB",
      "item_code": "MD105",
      "short_name": "MTN DATA BUNDLE",
      "fee": 0,
      "commission_on_fee": false,
      "label_name": "Mobile Number",
      "amount": 200
    }
  ]
}

Getting the customer value

There's one more important thing you need to create a bill payment: the customer parameter, which is an identifier for the customer with that service:

  • For airtime and mobile data bundles, the identifier is the customer's phone number.
  • For Wi-Fi Internet bundles, the identifier is the customer's account number on that service.
  • For electricity bills, the identifier is the customer's meter number.
  • For cable subscriptions, the identifier is the customer's decoder or smart card number.

You don't have to worry about hardcoding the identifier field in your code. As you can see in the examples above, the get bill categories endpoint returns a label_name field that contains a description of the identifier, so all you need to do is show the customer a text box and display that label next to it.

Validating a customer's details

You can use the validate bill service to check that the customer passed in the correct identifier (phone number, meter number, etc).

To do this, you'll need to pass the item_code (from the get bill categories response) in the URL and the customer identifier and code (the biller_code value from earlier) as query parameters.

For example, here's how that we can check that a phone number is valid for airtime recharge. You'll see that we're using the item_code and code (biller_code) from any of the airtime bill categories.

const details = {
    item_code: "AT099",
    code: "BIL099",
    customer: "08098291822",
};

flw.Bills.validate(details)
    .then(console.log)
    .catch(console.log)
details = {
    item_code: "AT099",
    code: "BIL099",
    customer: "08098291822",
}
response = bill.validate_bill_service details
curl --request POST 'https://api.flutterwave.com/v3/bill-items/AT099/validate?code=BIL099&&customer=08098291822' \
--header 'Authorization: Bearer YOUR_SECRET_KEY'

Here's what the response looks like. You'll notice that the name field in the response contains the network the number belongs to.

{
  "status": "success",
  "message": "Item validated successfully",
  "data": {
    "response_code": "00",
    "address": null,
    "response_message": "Successful",
    "name": "9MOBILE",
    "biller_code": "BIL099",
    "customer": "08098291822",
    "product_code": "AT099",
    "email": null,
    "fee": 0,
    "maximum": 0,
    "minimum": 0
  }
}
{
  "status": "error",
  "message": "Invalid customer id",
  "data": null
}

Examples

Here are a few examples of creating bill payments with our backend SDKs:

Airtime

When buying airtime, the type value will be "AIRTIME", and the customer value will be the customer's mobile number.

const details = {
    country: "NG",
    customer: "+23490803840303",
    amount: 1000,
    type: "AIRTIME",
    reference: generateTransactionReference(),
};
flw.Bills.create_bill(details)
    .then(console.log)
    .catch(console.log)
$billsService = new \Flutterwave\Bill();
$details = array(
    "country" => "NG",
    "customer" => "+23490803840303",
    "amount" => 1000,
    "type" => "AIRTIME",
    "reference" => generateTransactionReference(),
);
$response = $billsService->payBill($details);
bills = Bills.new(flw)
details = {
    country: "NG",
    customer: "+23490803840303",
    amount: 1000,
    type: "AIRTIME",
    reference: generate_transaction_reference,
}
response = bill.create_bill_payment details
print response
curl --request POST 'https://api.flutterwave.com/v3/bills' \
--header 'Authorization: Bearer YOUR_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
    "country": "NG",
    "customer": "+23490803840303",
    "amount": 1000,
    "type": "AIRTIME",
    "reference": "93kh049404e44"
 }'
{
  "status": "success",
  "message": "Bill payment successful",
  "data": {
    "phone_number": "+23490111840303",
    "amount": 1000,
    "network": "AIRTEL",
    "flw_ref": "CF-FLYAPI-20210413031924843012",
    "tx_ref": "BPUSSD1618327165983515"
  }
}

Data Bundles

When buying data bundles, the type value will be the biller name, and the customer value will be the customer's mobile number.

When buying data bundles, the amount must match the amount specified for that bill in the response from get bill categories.

const details = {
    country: "NG",
    customer: "0803xxxxxxx",
    amount: 100,
    type: "MTN 50 MB",
    reference: generateTransactionReference(),
};
flw.Bills.create_bill(details)
    .then(console.log)
    .catch(console.log)
$details = [
    "country" => "NG",
    "customer" => "0803xxxxxxx",
    "amount" => 100,
    "type" => "MTN 50 MB",
    "reference" => generateTransactionReference(),
];
$response = $billsService->payBill($details);
details = {
    country: "NG",
    customer: "0803xxxxxxx",
    amount: 100,
    type: "MTN 50 MB",
    reference: generate_transaction_reference,
}
response = bill.create_bill_payment details
curl --request POST 'https://api.flutterwave.com/v3/bills' \
--header 'Authorization: Bearer YOUR_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
    "country": "NG",
    "customer": "0803xxxxxxx",
    "amount": 100,
    "type": "MTN 50 MB",
    "reference": "93kh049404e44"
 }'
{
  "status": "success",
  "message": "Bill payment successful",
  "data": {
    "phone_number": "0803xxxxxxx",
    "amount": 100,
    "network": "MTN 50 MB",
    "flw_ref": "CF-FLYAPI-20210413092118616127",
    "tx_ref": "BP16183488797280007"
  }
}

Electricity

When paying electricity bills, the type value will be the biller name, and the customer value will be the customer's meter number.

const details = {
    country: "NG",
    customer: "9540803303",
    amount: 12000,
    type: "EKEDC PREPAID TOPUP",
    reference: generateTransactionReference(),
};
flw.Bills.create_bill(details)
    .then(console.log)
    .catch(console.log)
$details = [
    "country" => "NG",
    "customer" => "9540803303",
    "amount" => 12000,
    "type" => "EKEDC PREPAID TOPUP",
    "reference" => generateTransactionReference(),
];
$response = $billsService->payBill($details);
details = {
    country: "NG",
    customer: "9540803303",
    amount: 12000,
    type: "EKEDC PREPAID TOPUP",
    reference: generate_transaction_reference,
}
response = bill.create_bill_payment details
curl --request POST 'https://api.flutterwave.com/v3/bills' \
--header 'Authorization: Bearer YOUR_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
    "country": "NG",
    "customer": "9540803303",
    "amount": 12000,
    "type": "EKEDC PREPAID TOPUP",
    "reference": "93kh049404e44"
 }'
{
  "status": "success",
  "message": "Bill payment successful",
  "data": {
    "customer": "9540803303",
    "amount": 12000,
    "type": "EKEDC PREPAID TOPUP",
    "flw_ref": "CF-FLYAPI-20210413031924843012",
    "tx_ref": "BPUSSD1618327165983515"
  }
}

Cable TV

When buying cable TV subscriptions, the type value will be the biller name, and the customer value will be the customer's decoder number or smart card number.

const details = {
    country: "NG",
    customer: "231***********",
    amount: 1800,
    type: "DSTV Access",
    reference: generateTransactionReference(),
};
flw.Bills.create_bill(details)
    .then(console.log)
    .catch(console.log)
$details = [
    "country" => "NG",
    "customer" => "231***********",
    "amount" => 1800,
    "type" => "DSTV Access",
    "reference" => generateTransactionReference(),
];
$response = $billsService->payBill($details);
details = {
    country: "NG",
    customer: "231***********",
    amount: 1800,
    type: "DSTV Access",
    reference: generate_transaction_reference,
}
response = bill.create_bill_payment details
curl --request POST 'https://api.flutterwave.com/v3/bills' \
--header 'Authorization: Bearer YOUR_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
    "country": "NG",
    "customer": "231***********",
    "amount": 1800,
    "type": "DSTV Access",
    "reference": "93kh049404e44"
 }'
{
  "status": "success",
  "message": "Bill payment successful",
  "data": {
    "customer": "9540803303",
    "amount": 12000,
    "type": "EKEDC PREPAID TOPUP",
    "flw_ref": "CF-FLYAPI-20210413031924843012",
    "tx_ref": "BPUSSD1618327165983515"
  }
}

Bulk payments

You can also make bill payments in bulk using the bulk airtime purchase endpoint. A good example is if you need to buy airtime for multiple phone numbers at once. You'll pass in the following fields in the body:

  • bulk_data: an array of the separate airtime request objects (similar to what you would send for one payment)
  • bulk_reference: a reference code to identify this batch of payment
  • callback_url: an endpoint where we'll send you a response when each request in the batch is completed

Here's an example of a bulk airtime purchase:

const details = {
    bulk_reference: generateBulkTransactionReference(),
    callback_url: "https://webhook.site/5f9a659a-11a2-4925-89cf-8a59ea6a019a",
    bulk_data: [
        {
            country: "NG",
            customer: "+23490803840303",
            amount: 5200,
            type: "AIRTIME",
            reference: generateTransactionReference(),
        },
        {
            country: "NG",
            customer: "+23490111840303",
            amount: 1000,
            type: "AIRTIME",
            reference: generateTransactionReference(),
        }
    ]
};

flw.Bills.create_bulk(details)
    .then(console.log)
    .catch(console.log)
$details = [
    "bulk_reference" => generateBulkTransactionReference(),
    "callback_url" => "https://your-app.com/airtime-callback",
    "bulk_data" => [
        [
            "country" => "NG",
            "customer" => "+23490803840303",
            "amount" => 5200,
            "type" => "AIRTIME",
            "reference" => generateTransactionReference(),
        ],
        [
            "country" => "NG",
            "customer" => "+23490803840303",
            "amount" => 1000,
            "type" => "AIRTIME",
            "reference" => generateTransactionReference(),
        ],
    ],
];
$response = $billsService->bulkBill($details);
details = {
    bulk_reference: generate_bulk_transaction_reference,
    callback_url: "https://your-app.com/airtime-callback",
    bulk_data: [
        {
            country: "NG",
            customer: "+23490803840303",
            amount: 5200,
            type: "AIRTIME",
            reference: generate_transaction_reference,
        },
        {
            country: "NG",
            customer: "+23490803840303",
            amount: 1000,
            type: "AIRTIME",
            reference: generate_transaction_reference,
        },
    ]
}
response = bill.create_bulk_bill_payments details
print response
curl --request POST 'https://api.flutterwave.com/v3/bulk-bills' \
--header 'Authorization: Bearer YOUR_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
    "bulk_reference": "brd-93kh049404e44",
    "callback_url": "https://your-app.com/airtime-callback",
    "bulk_data": [
        {
            "country": "NG",
            "customer": "+23490803840303",
            "amount": 5200,
            "type": "AIRTIME",
            "reference": "53khd49404e44",
        },
        {
            "country": "NG",
            "customer": "+23490111840303",
            "amount": 1000,
            "type": "AIRTIME",
            "reference": "93kh049404e44",
        }
    ]
}'

The transactions will be queued, to be handled asynchronously

{
  "status": "success",
  "message": "Bulk bill Payment was queued for processing",
  "data": {
    "batch_reference": null
  }
}

Checking a payment's status

You can check its status of a bill payment afterwards with the get status endpoint. This is especially useful for bulk payments. To do this, you'll pass in the reference you generated earlier as a URL parameter.

const reference = "93kh049404e44";
flw.Bills.fetch_status({ reference })
    .then(console.log)
    .catch(console.log)
reference = "93kh049404e44"
response = bill.get_status_of_a_bill_payment reference
curl --request GET 'https://api.flutterwave.com/v3/bills/93kh049404e44' \
--header 'Authorization: Bearer YOUR_SECRET_KEY'

Here's what the response looks like. You'll notice that the name field in the response contains the network the number belongs to.

{
  "status": "success",
  "message": "Bill status fetch successful",
  "data": {
    "currency": "NGN",
    "customer_id": "+23490803840303",
    "frequency": "One Time",
    "amount": "1000.0000",
    "product": "AIRTIME",
    "product_name": "9MOBILE",
    "commission": 0,
    "transaction_date": "2021-04-13T22:59:40.16Z",
    "country": "NG",
    "tx_ref": "CF-FLYAPI-20210413105940530040",
    "extra": null,
    "product_details": "FLY-API-NG-AIRTIME-9MOBILE"
  }
{
  "status": "error",
  "message": "Transaction not found",
  "data": null
}

...And more

There's more you can do when it comes to paying bills on Flutterwave. See our API reference for the full rundown.

Loading...