ELITOP Developer API
A simple JSON API to sell airtime, data, cable TV, electricity and exam pins from your own website or app. Money comes from your ELITOP wallet. Get your API key.
Basics
Base URL: https://elitop.xyz/api/v1/
Authenticate every request with your key:
Authorization: Bearer YOUR_API_KEY Send POST bodies as JSON (Content-Type: application/json). Every response is JSON with status (true/false). Errors look like:
{ "status": false, "code": "insufficient_balance", "message": "Insufficient wallet balance. You need ₦990.00." } | HTTP | code | Meaning |
|---|---|---|
| 401 | missing_key, invalid_key | Key missing or wrong |
| 403 | api_access_off, ip_not_allowed | API not active, or your server IP is not whitelisted |
| 402 | insufficient_balance | Fund your wallet |
| 422 | rejected, bad_target, verification_failed... | Fix the request; the message says what |
| 429 | rate_limited | Too many requests per minute |
| 503 | unavailable | Maintenance or upgrade. Try again shortly |
Test keys
Create a test key in your dashboard to build safely. Test purchases are validated exactly like real ones but never spend money or contact a provider, and are not stored. The last 4 digits of the target choose the result: 2222 failed, 3333 still processing, anything else delivered.
Check balance
GETbalance.php
curl https://elitop.xyz/api/v1/balance.php -H "Authorization: Bearer YOUR_API_KEY"
{ "status": true, "data": { "balance": 15250.5, "currency": "NGN", "mode": "live" } } List products
GETproducts.php?service=airtime (service: airtime, data, cable, electricity, exam; leave out for all)
{ "status": true, "data": [
{ "product_id": 1, "service": "airtime", "network": "MTN", "name": "MTN Airtime", "pricing": "percent",
"you_pay_percent": 99, "min_amount": 50, "max_amount": 50000 },
{ "product_id": 42, "service": "data", "network": "MTN", "name": "MTN 1GB - 30 days", "pricing": "fixed", "price": 270 },
{ "product_id": 7, "service": "electricity", "network": "Ikeja", "name": "Ikeja Electric (IKEDC)", "pricing": "flat_fee",
"fee": 50, "min_amount": 1000, "max_amount": 500000 } ] } fixed: you pay price. percent: you pay that % of the amount you send. flat_fee: you pay the amount plus fee.
Verify a customer (cable, electricity, JAMB)
POSTverify.php Show the name to your customer before they pay. (purchase.php also verifies for you.)
| Field | |
|---|---|
| product_id | required |
| target | smartcard / meter number / JAMB profile code |
| meter_type | electricity only: prepaid or postpaid |
{ "status": true, "data": { "name": "JOHN DOE", "info": { "Address": "1 Example Street" } } } Buy
POSTpurchase.php
| Field | |
|---|---|
| request_id | required. Your own unique id for this purchase (6-64 characters). Sending the same request_id again never buys twice; you get the original order back with "duplicate": true. Safe to retry after a timeout. |
| product_id | required |
| target | required. Phone number (airtime/data), smartcard, meter number, JAMB profile code, or the phone to deliver exam pins to |
| amount | airtime and electricity only |
| coupon | optional discount coupon code. charge in the reply is what was taken from the wallet after the discount. |
| meter_type | electricity only: prepaid or postpaid |
curl -X POST https://elitop.xyz/api/v1/purchase.php \
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"request_id":"order-10045","product_id":1,"target":"08031234567","amount":500}'
{ "status": true, "data": {
"reference": "202610221530A1B2C3D4E5", "request_id": "order-10045", "status": "successful",
"service": "airtime", "product": "MTN Airtime", "target": "08031234567", "target_name": null,
"amount": 500, "charge": 495, "token": null, "message": "Delivered", "created_at": "2026-10-22 15:30:11", "completed_at": "2026-10-22 15:30:13" } } Order status values
successful | Delivered. Electricity tokens and exam pins are in token. |
processing | Not confirmed yet. Do not treat as failed and do not buy again. Wait for the callback or poll the order. The money is held and returned automatically if it fails. |
failed | Not delivered. Your wallet has already been refunded. |
refunded | A delivered order was later reversed and refunded. |
Get an order
GETorder.php?request_id=order-10045 or ?reference=...
GETorders.php?status=processing&page=1 (your API orders, 50 per page)
Callbacks (webhooks)
Set a callback URL in your dashboard (public https:// address). When an order finishes we POST:
{ "event": "order.successful", "data": { ...same fields as the order... }, "sent_at": "2026-10-22T15:30:13+01:00" } Events: order.successful, order.failed, order.refunded. Reply with any 2xx status. We retry failures after 1 min, 5 min, 30 min, 2 h and 12 h. Callbacks can arrive more than once; use reference to ignore repeats.
Every callback is signed. Check the X-ProPoint-Signature header (HMAC-SHA256 of the raw body with your signing secret) before trusting it:
<?php
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_PROPOINT_SIGNATURE'] ?? '';
if (!hash_equals(hash_hmac('sha256', $raw, 'YOUR_SIGNING_SECRET'), $sig)) { http_response_code(401); exit; }
$event = json_decode($raw, true);
// update your order using $event['data']['reference'] ...
http_response_code(200); Good practice
- Keep keys on your server only, never in a mobile app or web page. Whitelist your server IPs in the dashboard.
- Generate
request_idbefore calling and store it, so a timeout can be retried safely. - Treat
processingas "wait", not "failed". The final status arrives by callback or by pollingorder.php. - Limit: 120 requests per minute per key.