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.
type
and amount
values
Getting the 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
}
]
}
customer
value
Getting the 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 paymentcallback_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.