| Plan ID | Capacity | Validity | Price |
|---|
Get started
The Data API lets you sell mobile-data top-ups across every Nigerian network — MTN, Glo, Airtel, and 9mobile — straight from your backend. Token auth, IP whitelisting, idempotent retries, and a shared wallet that any of your apps can debit.
X-API-Key.Idempotency-Key./api/wallet/.Your first call — buy 1GB of MTN data:
curl --location 'https://amigo.ng/api/data/' \
--header 'X-API-Key: YOUR_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"network": 1,
"mobile_number": "09012345678",
"plan": 1001,
"Ported_number": true
}'
Overview
Amigo Developers gives you a single API to sell mobile-data top-ups:
- Data API — buy mobile data on MTN, Glo, Airtel, and 9mobile. Per-plan pricing, idempotent retries, real-time efficiency scores.
- Wallet balance New — a lightweight
GET /api/wallet/that returns your current live or test balance, so your backend can check funds before queuing a buy.
Base URL: https://amigo.ng/api.
All responses are JSON. Money is Naira (the wallet endpoint returns up to 2 decimals; the data endpoints use whole-naira amounts) and most responses include a pre-formatted display string like "₦4,000".
Authentication
Pass your token in any of these headers — whichever your HTTP client makes easiest:
X-API-Key: amg_live_xxxxxxxxxxxxxxxx # --or-- Authorization: Token amg_live_xxxxxxxxxxxxxxxx # --or-- Authorization: Bearer amg_live_xxxxxxxxxxxxxxxx
Each token belongs to a single Amigo user + API client. The user_id identifies whose wallet gets debited; the client_id identifies which merchant owns the resulting work.
Test mode New
Build and integrate against a sandbox before touching real money. Test-mode tokens are intentionally familiar (Stripe-style) — the token prefix decides the environment:
| Token prefix | Mode | Wallet | Order references | Inventory |
|---|---|---|---|---|
amg_test_… |
test | Sandbox wallet — fake money, starts at ₦1,000,000. | AMG-COM-TEST-… |
Never touched — test orders don't affect live availability. |
amg_live_… (or legacy) |
live | Your Amigo wallet (real funds). | AMG-COM-… |
Decremented at order time, restored on cancel. |
Total isolation. A test token only sees test orders and test webhook endpoints; a live token only sees live ones. The two namespaces never cross. Every webhook payload includes a livemode boolean so subscribers can route or filter.
Refill the sandbox wallet:
Refills the sandbox wallet back to ₦1,000,000. Requires an amg_test_ token — live tokens get 403 live_token_not_allowed.
curl -X POST "https://amigo.ng/api/commerce/sandbox/reset-balance/" \
-H "X-API-Key: $TEST_TOKEN"
# → { "success": true, "data": { "test_balance": 1000000, "display": "₦1,000,000", "livemode": false } }
Wallet balance New
Check the balance your token can spend, in the current mode. Useful for showing a low-balance
warning in your dashboard, gating a data buy before you call /api/data/,
or reconciling against your own ledger.
Mode follows the token: amg_live_… reads your live wallet,
amg_test_… reads the sandbox. There's no cross-mode peek with one token.
Response is never cached — it always reflects the latest debit, credit, or refund.
curl "https://amigo.ng/api/wallet/" \
-H "X-API-Key: $TOKEN"
# →
{
"success": true,
"data": {
"balance": 168.50,
"display": "₦168.50",
"currency": "NGN",
"livemode": true,
"mode": "live"
}
}
| Field | Type | Notes |
|---|---|---|
balance | number | Naira, up to 2 decimal places. |
display | string | Pre-formatted for UI, e.g. "₦168.50". |
currency | string | Always "NGN" today. |
livemode | boolean | true for live tokens, false for test. |
mode | string | "live" or "test" — convenience over livemode. |
Errors
Every non-2xx response uses the same envelope:
{
"success": false,
"error": "insufficient_balance",
"message": "Wallet balance is below the order total."
}
| HTTP | error | When |
|---|---|---|
| 401 | invalid_token | Missing, expired, or revoked token. |
| 403 | ip_not_whitelisted | Your client has a whitelist and your IP isn't on it. |
| 404 | not_found | Product, order, or endpoint doesn't exist (or isn't yours). |
| 422 | invalid_* | Body validation failed; message explains. |
| 422 | insufficient_balance | Wallet is below the order total. |
| 422 | out_of_stock | Item ran out between catalog read and order create. |
| 429 | rate_limited | Too many requests in this minute window. |
| 500 | server_error | Unexpected. Logged on our end; retry with backoff. |
Idempotency
The Data API accepts an Idempotency-Key header on every state-changing POST. Retry with the same key and you get back the original response — no double-debit, no duplicate work.
curl -X POST https://amigo.ng/api/data/ \ -H "X-API-Key: $TOKEN" \ -H "Idempotency-Key: data-2026-05-17-7f3a" \ -H "Content-Type: application/json" \ --data @buy.json
Keys are opaque strings up to 80 chars. Scope is per-token; reuse across tokens has no effect. Cache TTL is 24h.
Data API Mobile data top-ups
One endpoint: POST /api/data/. Pick a plan from the live catalog below, send it with the customer's mobile number, and we route the top-up through the carrier.
POST /api/data/
Body:
{
"network": 1, // 1=MTN, 2=Glo, 4=Airtel, 3=9mobile
"mobile_number": "09012345678",
"plan": 1001, // from /plans_catalog.php
"Ported_number": true // if MNP'd
}
<?php
$ch = curl_init('https://amigo.ng/api/data/');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: YOUR_API_TOKEN',
'Content-Type: application/json',
'Idempotency-Key: '.bin2hex(random_bytes(16))
],
CURLOPT_POSTFIELDS => json_encode([
'network' => 1,
'mobile_number' => '09012345678',
'plan' => 1001,
'Ported_number' => true
])
]);
echo curl_exec($ch);
fetch('https://amigo.ng/api/data/', {
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_TOKEN',
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({
network: 1,
mobile_number: '09012345678',
plan: 1001,
Ported_number: true
})
}).then(r => r.json()).then(console.log);
curl -X POST https://amigo.ng/api/data/ \
-H "X-API-Key: $TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
--data '{"network":1,"mobile_number":"09012345678","plan":1001,"Ported_number":true}'
Live plan catalog
These tables refresh on every page load from the same plan APIs your integration can use. Use the Plan ID column when calling POST /data/.
| Plan ID | Capacity | Validity | Price |
|---|
| Plan ID | Capacity | Validity | Price |
|---|
Status (requery) New
Look up the current state of a single data purchase you already made. Use this when the original
POST /api/data/ response was delayed or you want to reconcile against
your own ledger before refunding a customer.
Scope: a token only sees purchases owned by its own client. Requests for a
reference that belongs to another client return a clean 404 — same
shape as "doesn't exist anywhere" — so reference enumeration is blocked.
curl "https://amigo.ng/api/data/status/?ref=AMG-20260618215802-ad5475" \
-H "X-API-Key: $TOKEN"
# →
{
"success": true,
"data": {
"reference": "AMG-20260618215802-ad5475",
"status": "delivered",
"network": 1,
"mobile_number": "09064703618",
"plan": 3333,
"amount_charged": 900,
"message": "3GB delivered to 09064703618 successfully.",
"purchased_at": "2026-06-18T21:58:02+00:00",
"queried_at": "2026-06-21T11:34:59+00:00",
"duration_ms": 2672,
"original_http_status": 200
}
}
| Field | Type | Notes |
|---|---|---|
reference | string | The Amigo reference you received from the original POST /data/. |
status | string | delivered or failed. failed means we did not debit your wallet. |
network | int | 1=MTN, 2=Glo, 4=Airtel, 3=9mobile. |
mobile_number | string | The recipient you originally targeted. |
plan | int | The plan id from the live catalog. |
amount_charged | number | What Amigo debited from your wallet, in Naira. 0 for failed transactions. |
message | string | Short human-readable summary. Amigo writes this — never the underlying provider — so it's safe to surface to your end users. |
purchased_at | string | ISO 8601 UTC timestamp of the original purchase. |
queried_at | string | ISO 8601 UTC timestamp of this requery call. |
duration_ms | int | How long the original vend took (provider round-trip). |
original_http_status | int | The HTTP status the original POST /data/ returned. |
Error responses
| HTTP | error | When |
|---|---|---|
| 404 | not_found | No purchase with that reference is owned by your client. |
| 422 | missing_ref | ?ref= was empty. |
| 422 | invalid_ref | Reference contains illegal characters or is over 80 chars. |
Plan efficiency
Reliability scores so you can pick plans that are safer for production traffic. A score ≥ 98% is excellent; < 75% is poor and we recommend avoiding.
| Plan ID | Capacity | Efficiency |
|---|
| Plan ID | Capacity | Efficiency |
|---|
| Plan ID | Capacity | Efficiency |
|---|