Skip to main content

Buy airtime or data

POST/airtime/v1/purchases

Buys airtime or a data bundle for one number. No added fees: $1.00 of airtime costs $1.00, and a bundle costs its listed price. The response shows whether the purchase succeeded, failed or is pending.

Your key needs the Airtime API turned on, and in live your team must be approved. See Buying airtime and data bundles.

Request body​

Content-Type: application/json

Send amount for airtime or a bundle for data. A request with both or neither is refused with 400.

Name a bundle by productId (its id), by productCode (its code), or by both. If you send both, they must name the same bundle. Otherwise the request is refused with 400 and nothing is charged. Store the id, because it never changes.

  • #msisdnstringRequired
    The number to buy for. International (231 + 9 digits) or local (0 + 9 digits). Orange numbers start with 2317. Lonestar Cell MTN numbers start with 23188 or 23155. Other networks are refused.
    Example: 231770000000
  • #amountnumberOptional
    For airtime. The airtime the number receives, in US dollars. At least 1 and at most your team's limit per recipient (50 unless agreed otherwise). Your account is charged this amount. Leave it out when you buy a bundle.
    Example: 5
  • #productIdstringOptional
    For a data bundle. A bundle id (prod_…) from GET /products. The bundle must be on sale, for the number's network, and priced within your team's limit per recipient. You pay its price. Leave it out when you send amount.
    Example: prod_p4nzx9xp335h
  • #productCodestringOptional
    For a data bundle, instead of or with productId. A bundle code from GET /products. With productId, it must name the same bundle. Leave it out when you send amount.
    Example: mtn-data-15gb-30d
  • #expectedPricenumberOptional
    For a data bundle, the price you showed your customer, in US dollars, with up to 2 decimal places. If the bundle's price is different now, the purchase is refused with 409 (E03004) and nothing is charged. Leave it out when you send amount.
    Example: 9
  • #workspacestringOptional
    The handle of the workspace to buy for and charge, such as gbarnga-branch. Leave it out for Main. See Workspaces.
    Example: gbarnga-branch
  • #externalIdstringRequired
    Your own reference for the purchase, such as an order number. 1 to 30 letters, digits, "_", "." or "-", unique within your team. Reusing one returns the original purchase without charging again.
    Example: order-8842
Retrying safely

If a request times out, send it again with the same externalId. If the first request went through, you get it back with 200. You're charged once. See Retries and externalId.

Example request​

curl -X POST "https://sandbox-api.ponitor.com/airtime/v1/purchases" \
-H "apiKey: $PONITOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"msisdn": "231880000000",
"productId": "prod_p4nzx9xp335h",
"externalId": "order-8843"
}'

To buy by code, send "productCode": "mtn-data-15gb-30d" instead of productId. To buy airtime, send "amount": 5 instead.

Responses​

Created. Ponitor accepted the purchase, which can still be FAILED. The body is the purchase. status is usually SUCCESS. PENDING means the network hasn't confirmed yet. A webhook tells you when it settles, or you can call Get a purchase. An airtime purchase has the same shape, with type AIRTIME and product null.

{
"id": "6f1c2a3e-8b4d-4c1a-9e2f-3a4b5c6d7e8f",
"externalId": "order-8843",
"type": "DATA",
"msisdn": "231880000000",
"carrier": "LONESTAR",
"product": {
"id": "prod_p4nzx9xp335h",
"code": "mtn-data-15gb-30d",
"name": "Lonestar Cell MTN 15GB, 30 days",
"data": "15GB",
"validity": "30 days",
"price": 9,
"currency": "USD"
},
"amount": 9,
"cost": 9,
"status": "SUCCESS",
"failureReason": null,
"instructions": null,
"createdAt": "2026-10-20T19:40:00.000Z"
}

A FAILED purchase also returns 201. The request was valid, but the network declined it. The cost is refunded, and failureReason says why. See Why a purchase failed.

The purchase object​

FieldDescription
idPonitor's ID for the purchase. Use it with GET /purchases/{id}.
externalIdThe reference you sent.
typeWhat was bought, AIRTIME or DATA.
msisdnThe number, normalised to international format.
carrierORANGE or LONESTAR (Lonestar Cell MTN), detected from the number.
productFor DATA, the bundle as it was when bought, with id, code, name, data, validity, price and currency. It doesn't change if the bundle later changes. null for airtime.
amountFor airtime, the airtime delivered in USD. For data, the bundle's price.
costWhat your account was charged, in USD. No added fees: for airtime it equals amount, and for data it's the bundle's price. Refunded if the purchase fails.
statusSUCCESS, PENDING or FAILED. See What happens to a purchase.
failureReasonWhy a FAILED purchase failed. See Why a purchase failed. null otherwise.
instructionsText from the network for the subscriber, such as how to use a bundle. Pass it on when it's set. Usually null.
createdAtWhen the purchase was made.

Every purchase has type, product and instructions, including airtime purchases.

Refused purchases​

A refused purchase answers 400, and nothing is charged. Fix the request and send it again.

ProblemCodeMessage
A field is missing or invalid, such as msisdnE01001Each field's message is in details, for example msisdn must be 12 digits starting with 231 or 10 digits starting with 0
productId isn't a bundle id, for example a code sent as productIdE01001productId must be a product id from GET /products (prod_…); send a code as productCode
productCode isn't a bundle codeE01001productCode must be a product code from GET /products
amount under $1E01001amount must be at least 1.00
amount with productId or productCodeE01002Send either amount (Airtime) or a product (productId or productCode), not both
Neither amount nor a bundleE01002amount (Airtime) or a product (productId or productCode) is required
productId and productCode name different bundlesE01002productId prod_p4nzx9xp335h and productCode mtn-data-500mb-3d are different products; send one, or both for the same product
amount or the bundle's price is over your team's limit per recipientE01002$99.00 exceeds your team's $50.00 per-recipient limit
The bundle is unknown or withdrawnE12001Unknown or unavailable product: prod_p4nzx9xp335h (the id or code you sent)
The bundle is for the other networkE12002Product mtn-data-15gb-30d is for Lonestar Cell MTN numbers (or Orange numbers)
The number isn't on Orange or Lonestar Cell MTNE12003msisdn is not on a supported network (Orange or Lonestar Cell MTN)
Your account can't cover the costE03001Insufficient account balance. Top up under Billing to continue.

Your team's limit per recipient is $50 unless agreed otherwise. It applies to airtime and to a bundle's price. Contact us to raise it.