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.

Token authBearer token via X-API-Key.
IP whitelistLock tokens to trusted servers in production.
Idempotent retriesSafe retries with Idempotency-Key.
Shared walletOne balance debited per call — check it via /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.

Keep tokens out of source control. Generate one per environment (staging vs. production), and rotate immediately if leaked. Whitelisting your IPs is supported — ask support to enable it on your client.

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 prefixModeWalletOrder referencesInventory
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:

POST /api/commerce/sandbox/reset-balance/ Test mode only

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 } }
Recommended integration flow. Wire up your storefront with a test token, place a few orders, watch the webhook events fire to your test endpoint, exercise cancel/refund. When everything looks right, swap to a live token — same API surface, same response shapes, real money this time.

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.

GET /api/wallet/ Live + test

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"
  }
}
FieldTypeNotes
balancenumberNaira, up to 2 decimal places.
displaystringPre-formatted for UI, e.g. "₦168.50".
currencystringAlways "NGN" today.
livemodebooleantrue for live tokens, false for test.
modestring"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."
}
HTTPerrorWhen
401invalid_tokenMissing, expired, or revoked token.
403ip_not_whitelistedYour client has a whitelist and your IP isn't on it.
404not_foundProduct, order, or endpoint doesn't exist (or isn't yours).
422invalid_*Body validation failed; message explains.
422insufficient_balanceWallet is below the order total.
422out_of_stockItem ran out between catalog read and order create.
429rate_limitedToo many requests in this minute window.
500server_errorUnexpected. 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/

POST /api/data/ Idempotent

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/.

MTN (network 1)
MTN Plans
Plan IDCapacityValidityPrice
Glo (network 2)
Glo Plans
Plan IDCapacityValidityPrice
Airtel (network 4)
Airtel Plans
Plan IDCapacityValidityPrice

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.

GET /api/data/status/?ref=AMG-… Live + test

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
  }
}
FieldTypeNotes
referencestringThe Amigo reference you received from the original POST /data/.
statusstringdelivered or failed. failed means we did not debit your wallet.
networkint1=MTN, 2=Glo, 4=Airtel, 3=9mobile.
mobile_numberstringThe recipient you originally targeted.
planintThe plan id from the live catalog.
amount_chargednumberWhat Amigo debited from your wallet, in Naira. 0 for failed transactions.
messagestringShort human-readable summary. Amigo writes this — never the underlying provider — so it's safe to surface to your end users.
purchased_atstringISO 8601 UTC timestamp of the original purchase.
queried_atstringISO 8601 UTC timestamp of this requery call.
duration_msintHow long the original vend took (provider round-trip).
original_http_statusintThe HTTP status the original POST /data/ returned.

Error responses

HTTPerrorWhen
404not_foundNo purchase with that reference is owned by your client.
422missing_ref?ref= was empty.
422invalid_refReference 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.

MTN
MTN Efficiency
Plan IDCapacityEfficiency
Glo
Glo Efficiency
Plan IDCapacityEfficiency
Airtel
Airtel Efficiency
Plan IDCapacityEfficiency