Submefast VTU API
Sell airtime, data, cable TV, electricity, exam PINs, recharge and data cards, and betting top-ups from your own website or app — paid from your Submefast wallet at your account's prices.
Base URLhttps://sub.submefast.com/api/v1
- Create a Submefast account and ask us to upgrade it to a Vendor account.
- Log in, open Developer API and generate your Live API Key (it starts with
sub_live_). Keep it on your server — never in a browser or mobile app. - Fund your wallet. Every purchase is charged from it.
- Call the catalog for plan IDs, then make purchases with your key and your 4-digit transaction PIN.
curl https://sub.submefast.com/api/v1/balance.php \ -H 'Authorization: Bearer YOUR_API_KEY'
Authentication
Send your API key in the Authorization header on every request. Purchase requests are POST with a JSON body.
Authorization: Bearer YOUR_API_KEY Content-Type: application/json
- Every purchase also needs your 4-digit transaction
pinin the body. Three wrong PINs lock purchases for 10 minutes. - Your key stops working if you change or reset your password or generate a new key. Generate a new one in Developer API and update your server.
- A missing, wrong or revoked key returns
401:{"status":"error","success":false,"message":"Unauthorized: Invalid or missing token"}.
Responses & status
Every response is JSON with a status of success, pending or error, and a readable message. Money is in naira.
| status | HTTP | What it means |
|---|---|---|
success | 200 | The purchase was delivered. data.reference is your receipt number. |
pending | 200 | Paid and sent to the provider, waiting for confirmation. Check it with Transaction status until it is final. If it fails, your wallet is refunded automatically. |
error + refunded: true | 400 | The provider declined it. Your wallet has already been refunded. reference identifies the refunded transaction. |
error | 400 | Nothing was bought — wrong input, wrong PIN, insufficient balance, a limit reached, or a duplicate request. Read message. |
error | 401 / 403 / 405 / 429 | Bad key / service not available on this account / wrong HTTP method / too many requests. |
For pending purchases, set a webhook to be told when they become final, or check Transaction status (for example every 30–60 seconds).
Webhooks
Save a webhook URL under Developer API → Webhook Configuration. When a purchase that returned pending becomes final, we POST this JSON to it (failed deliveries are retried for up to a day):
{
"event": "transaction.success",
"reference": "SUB7E1BC10B4B3D1791190455",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"status": "success",
"refunded": false,
"category": "airtime",
"amount": 490.0,
"description": "₦500 MTN airtime to 08031234567"
},
"timestamp": "2026-10-08T14:15:02Z"
}eventistransaction.successortransaction.failed. A failed purchase is refunded to your wallet (refunded: true).- Verify it came from us: the
X-Signatureheader is the hex HMAC-SHA256 of the raw request body, using your API key as the secret. Compare it in constant time and ignore requests that don't match. - Reply with any
2xxstatus. Handle repeats safely — usereferenceto ignore an event you already processed. - Need the electricity token or PINs? Fetch them with Transaction status.
- The URL must be a public internet address (
https://recommended).
$body = file_get_contents('php://input');
$sig = hash_hmac('sha256', $body, getenv('API_KEY'));
if (!hash_equals($sig, $_SERVER['HTTP_X_SIGNATURE'] ?? '')) { http_response_code(401); exit; }
$event = json_decode($body, true);
// mark $event['reference'] as $event['data']['status'] in your system
http_response_code(200);Limits & duplicates
- Purchases: up to 30 requests per minute per account. Meter and smartcard checks: 20 per 5 minutes. Over the limit returns
429. - Daily spending limits apply per account (your account level's limit, set by us). Reaching one returns
"Transaction limit reached. Contact Admin." - Duplicate protection: the same purchase (for example the same number, network and amount) sent again while the first is still pending, or within 30 seconds after it succeeded, is refused instead of charged twice. Wait and check the first one with Transaction status.
- Phone numbers are 11-digit local numbers (
08031234567), not+234….
Wallet balance
GET https://sub.submefast.com/api/v1/balance.php
Your current wallet and cashback balance.
curl -X GET 'https://sub.submefast.com/api/v1/balance.php' \ -H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://sub.submefast.com/api/v1/balance.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/balance.php', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
},
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.get(
'https://sub.submefast.com/api/v1/balance.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"data": {
"wallet_balance": 152830.5,
"cashback_balance": 120.0,
"currency": "NGN"
}
}Service catalog
GET https://sub.submefast.com/api/v1/catalog.php
Data plans, cable TV plans, electricity companies, exam PIN and recharge-card prices — priced for your account level. Call this to get the IDs the purchase endpoints need.
Use data_plans[].id as plan_id for Buy data, cable_plans[].id as plan_id for cable TV, cable_plans[].cable_id for smartcard validation and discos[].disco_id for electricity. The catalog's own plan_id field is an internal code — don't send it.
curl -X GET 'https://sub.submefast.com/api/v1/catalog.php' \ -H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://sub.submefast.com/api/v1/catalog.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/catalog.php', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
},
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.get(
'https://sub.submefast.com/api/v1/catalog.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"data": {
"data_plans": [
{
"id": 41,
"network": "MTN",
"plan_id": "211",
"plan_name": "MTN SME 1GB",
"display_name": "1GB",
"data_type": "SME",
"data_size": "1GB",
"validity": "30 Days",
"selling_price": "250.00",
"network_id": 1,
"cashback_type": null,
"cashback_value": null
}
],
"cable_plans": [
{
"id": 7,
"provider": "GOTV",
"cable_id": 1,
"plan_id": "gotv-max",
"plan_name": "GOtv Max",
"display_name": null,
"selling_price": "8500.00"
}
],
"discos": [
{
"disco_id": 1,
"disco_name": "Ikeja Electric",
"charge": "100.00"
}
],
"exam_prices": {
"WAEC": 3500.0,
"NECO": 1300.0
},
"recharge_card_prices": {
"MTN": {
"100": 98.0,
"200": 196.0
}
},
"bulk_sms_pricing": null,
"cashback": {
"airtime": null,
"data": null
}
}
}Buy airtime
POST https://sub.submefast.com/api/v1/airtime.php
Send airtime to any Nigerian number. You are charged the face value less your account level's discount.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
network | string | required | MTN, GLO, AIRTEL or 9MOBILE (not case-sensitive). |
phone | string | required | 11-digit local number, e.g. 08031234567 (not +234…). |
amount | number | required | Between ₦50 and ₦10,000. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
curl -X POST 'https://sub.submefast.com/api/v1/airtime.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"network":"MTN","phone":"08031234567","amount":500,"pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/airtime.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'network' => 'MTN',
'phone' => '08031234567',
'amount' => 500,
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/airtime.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"network": "MTN",
"phone": "08031234567",
"amount": 500,
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/airtime.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"network": "MTN",
"phone": "08031234567",
"amount": 500,
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"message": "₦500 MTN airtime sent to 08031234567",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"cashback_earned": 0.0,
"balance_before": 152830.5,
"balance_after": 152340.5
}
}{
"status": "pending",
"success": true,
"pending": true,
"message": "Transaction is being processed. Check receipt for updates.",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455"
}
}{
"status": "error",
"success": false,
"message": "The provider could not complete this request. Your wallet has been refunded.",
"refunded": true,
"reference": "SUB7E1BC10B4B3D1791190455"
}Buy data
POST https://sub.submefast.com/api/v1/data.php
Send a data bundle. The network comes from the plan; the price is your account level's plan price.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
network | string | required | The plan's network (MTN, GLO, AIRTEL, 9MOBILE). |
phone | string | required | 11-digit local number. |
plan_id | integer | required | data_plans[].id from the catalog. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
promo_code | string | optional | A promo code, if you have one (one use per account). |
curl -X POST 'https://sub.submefast.com/api/v1/data.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"network":"MTN","phone":"08031234567","plan_id":41,"pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/data.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'network' => 'MTN',
'phone' => '08031234567',
'plan_id' => 41,
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/data.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"network": "MTN",
"phone": "08031234567",
"plan_id": 41,
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/data.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"network": "MTN",
"phone": "08031234567",
"plan_id": 41,
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"message": "1GB MTN data sent to 08031234567",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"cashback_earned": 0.0,
"balance_before": 152830.5,
"balance_after": 152580.5
}
}{
"status": "pending",
"success": true,
"pending": true,
"message": "Transaction is being processed. Check receipt for updates.",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455"
}
}{
"status": "error",
"success": false,
"message": "The provider could not complete this request. Your wallet has been refunded.",
"refunded": true,
"reference": "SUB7E1BC10B4B3D1791190455"
}Validate meter
POST https://sub.submefast.com/api/v1/validate_meter.php
Look up the customer name on a meter before you buy. Free.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
disco_id | integer | required | discos[].disco_id from the catalog. |
meter_number | string | required | The meter number. |
meter_type | string | required | prepaid or postpaid (lowercase). |
curl -X POST 'https://sub.submefast.com/api/v1/validate_meter.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"disco_id":1,"meter_number":"45031234567","meter_type":"prepaid"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/validate_meter.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'disco_id' => 1,
'meter_number' => '45031234567',
'meter_type' => 'prepaid',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/validate_meter.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"disco_id": 1,
"meter_number": "45031234567",
"meter_type": "prepaid"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/validate_meter.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"disco_id": 1,
"meter_number": "45031234567",
"meter_type": "prepaid"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"data": {
"name": "ADEBAYO JOHN",
"address": "12 ALLEN AVENUE IKEJA"
}
}{
"status": "error",
"message": "Invalid meter number"
}Buy electricity
POST https://sub.submefast.com/api/v1/electricity.php
Buy a prepaid token or pay a postpaid bill. Your wallet is charged the amount plus the electricity company's service charge.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
disco_id | integer | required | discos[].disco_id from the catalog. |
meter_number | string | required | The meter number. |
meter_type | string | required | prepaid or postpaid (lowercase). |
amount | number | required | Between ₦1,000 and ₦50,000. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
phone | string | optional | Customer phone number (some providers send the token by SMS). |
If the provider confirms the payment but sends no token, token is "Check meter". Get the token later from Transaction status.
curl -X POST 'https://sub.submefast.com/api/v1/electricity.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"disco_id":1,"meter_number":"45031234567","meter_type":"prepaid","amount":5000,"pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/electricity.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'disco_id' => 1,
'meter_number' => '45031234567',
'meter_type' => 'prepaid',
'amount' => 5000,
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/electricity.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"disco_id": 1,
"meter_number": "45031234567",
"meter_type": "prepaid",
"amount": 5000,
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/electricity.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"disco_id": 1,
"meter_number": "45031234567",
"meter_type": "prepaid",
"amount": 5000,
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"success": true,
"message": "Token: 1234-5678-9012-3456-7890",
"pending": false,
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"token": "1234-5678-9012-3456-7890",
"cashback_earned": 0.0,
"pending": false,
"balance_before": 152830.5,
"balance_after": 147730.5
}
}{
"status": "pending",
"success": true,
"message": "Transaction is being processed. Check receipt for updates.",
"pending": true,
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"token": null,
"cashback_earned": 0.0,
"pending": true,
"balance_before": 152830.5,
"balance_after": 147730.5
}
}{
"status": "error",
"success": false,
"message": "The provider could not complete this request. Your wallet has been refunded.",
"refunded": true,
"reference": "SUB7E1BC10B4B3D1791190455"
}Validate smartcard
POST https://sub.submefast.com/api/v1/validate-cable.php
Look up the customer name on a smartcard / IUC number before you subscribe. Free.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
cable_id | integer | required | 1 = GOTV, 2 = DSTV, 3 = STARTIMES (cable_plans[].cable_id). |
smartcard | string | required | Smartcard / IUC number, digits only. |
curl -X POST 'https://sub.submefast.com/api/v1/validate-cable.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"cable_id":2,"smartcard":"7023456789"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/validate-cable.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'cable_id' => 2,
'smartcard' => '7023456789',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/validate-cable.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"cable_id": 2,
"smartcard": "7023456789"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/validate-cable.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"cable_id": 2,
"smartcard": "7023456789"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"success": true,
"message": "SmartCard verified successfully.",
"data": {
"name": "ADEBAYO JOHN",
"customer_name": "ADEBAYO JOHN",
"smartcard": "7023456789"
}
}{
"status": "error",
"success": false,
"message": "Invalid smartcard number"
}Subscribe cable TV
POST https://sub.submefast.com/api/v1/cabletv.php
Renew or buy a DSTV, GOTV or Startimes bouquet. The provider comes from the plan.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
plan_id | integer | required | cable_plans[].id from the catalog. |
smartcard | string | required | Smartcard / IUC number. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
phone | string | optional | Customer phone number. |
curl -X POST 'https://sub.submefast.com/api/v1/cabletv.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"plan_id":7,"smartcard":"7023456789","pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/cabletv.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'plan_id' => 7,
'smartcard' => '7023456789',
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/cabletv.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"plan_id": 7,
"smartcard": "7023456789",
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/cabletv.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"plan_id": 7,
"smartcard": "7023456789",
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"message": "GOtv Max subscription successful",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"cashback_earned": 0.0,
"balance_before": 152830.5,
"balance_after": 144330.5
}
}{
"status": "pending",
"success": true,
"pending": true,
"message": "Transaction is being processed. Check receipt for updates.",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455"
}
}{
"status": "error",
"success": false,
"message": "The provider could not complete this request. Your wallet has been refunded.",
"refunded": true,
"reference": "SUB7E1BC10B4B3D1791190455"
}Buy exam PINs
POST https://sub.submefast.com/api/v1/exam_pin.php
Result-checker PINs. Price per PIN is in exam_prices in the catalog.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
exam_name | string | required | One of: WAEC, NECO, NABTEB. |
quantity | integer | required | 1 to 5. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
curl -X POST 'https://sub.submefast.com/api/v1/exam_pin.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"exam_name":"WAEC","quantity":1,"pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/exam_pin.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'exam_name' => 'WAEC',
'quantity' => 1,
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/exam_pin.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"exam_name": "WAEC",
"quantity": 1,
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/exam_pin.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"exam_name": "WAEC",
"quantity": 1,
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"message": "1 WAEC PIN(s) purchased",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"pins": [
"PIN: 123456789012 | Serial: WRN123456789"
],
"balance_before": 152830.5,
"balance_after": 149330.5
}
}{
"status": "pending",
"success": true,
"pending": true,
"message": "Transaction is being processed. Check receipt for updates.",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455"
}
}{
"status": "error",
"success": false,
"message": "Purchase failed. Wallet reversed.",
"refunded": true,
"reference": "SUB7E1BC10B4B3D1791190455"
}Buy recharge cards
POST https://sub.submefast.com/api/v1/recharge_card.php
Printable airtime recharge PINs. GET on the same URL lists networks and available denominations.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
network_id | integer | required | 1 = MTN, 2 = GLO, 3 = AIRTEL, 4 = 9MOBILE. |
denomination | integer | required | 100, 200, 500 or 1000 (whichever are available — see the GET). |
quantity | integer | optional | 1 to 50. Default 1. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
name_on_card | string | optional | Business name printed on the cards (max 30 characters). |
curl -X POST 'https://sub.submefast.com/api/v1/recharge_card.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"network_id":1,"denomination":100,"quantity":2,"pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/recharge_card.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'network_id' => 1,
'denomination' => 100,
'quantity' => 2,
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/recharge_card.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"network_id": 1,
"denomination": 100,
"quantity": 2,
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/recharge_card.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"network_id": 1,
"denomination": 100,
"quantity": 2,
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"message": "2 card(s) ready",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"pins": [
{
"pin": "1234567890123456",
"serial": "SN000123"
},
{
"pin": "6543210987654321",
"serial": "SN000124"
}
],
"balance_before": 152830.5,
"balance_after": 152634.5
}
}{
"status": "pending",
"success": true,
"pending": true,
"message": "Transaction is being processed. Check receipt for updates.",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455"
}
}{
"status": "error",
"message": "This card type is not available"
}Buy data cards
POST https://sub.submefast.com/api/v1/data_card.php
Printable data PINs for a data plan.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
plan_id | integer | required | data_plans[].id from the catalog. |
quantity | integer | optional | 1 to 5. Default 1. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
name_on_card | string | optional | Business name printed on the cards (max 30 characters). |
curl -X POST 'https://sub.submefast.com/api/v1/data_card.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"plan_id":41,"quantity":1,"pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/data_card.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'plan_id' => 41,
'quantity' => 1,
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/data_card.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"plan_id": 41,
"quantity": 1,
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/data_card.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"plan_id": 41,
"quantity": 1,
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"message": "1 data card(s) ready",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"pins": [
{
"pin": "1234567890",
"memo": "Dial *460*6*1# to load"
}
],
"balance_before": 152830.5,
"balance_after": 152580.5
}
}{
"status": "pending",
"success": true,
"pending": true,
"message": "Transaction is being processed. Check receipt for updates.",
"data": {
"reference": "SUB7E1BC10B4B3D1791190455"
}
}{
"status": "error",
"success": false,
"message": "The provider could not complete this request. Your wallet has been refunded.",
"refunded": true,
"reference": "SUB7E1BC10B4B3D1791190455"
}Betting platforms
GET https://sub.submefast.com/api/v1/betting.php
Platforms you can fund, with their service charge.
curl -X GET 'https://sub.submefast.com/api/v1/betting.php' \ -H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://sub.submefast.com/api/v1/betting.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/betting.php', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
},
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.get(
'https://sub.submefast.com/api/v1/betting.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"success": true,
"platforms": [
{
"id": 3,
"platform_name": "Bet9ja",
"charge": "100.00"
}
]
}Fund betting wallet
POST https://sub.submefast.com/api/v1/betting.php
Top up a customer's betting account. Your wallet is charged the amount plus the platform charge. To check the customer first, send "action": "validate_customer" with plan_id and customer_id only.
Body (JSON)
| Field | Type | Description | |
|---|---|---|---|
plan_id | integer | required | Platform id from Betting platforms. |
customer_id | string | required | The customer's ID on the platform. |
amount | number | required | Between ₦100 and ₦5,000. |
pin | string | required | Your 4-digit transaction PIN (the one you use on the website). 3 wrong PINs lock purchases for 10 minutes. |
customer_name | string | optional | From validation, if you already have it. |
curl -X POST 'https://sub.submefast.com/api/v1/betting.php' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"plan_id":3,"customer_id":"12345678","amount":1000,"pin":"1234"}'<?php
$ch = curl_init('https://sub.submefast.com/api/v1/betting.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(array (
'plan_id' => 3,
'customer_id' => '12345678',
'amount' => 1000,
'pin' => '1234',
)),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
'Content-Type: application/json',
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/betting.php', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"plan_id": 3,
"customer_id": "12345678",
"amount": 1000,
"pin": "1234"
}),
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.post(
'https://sub.submefast.com/api/v1/betting.php',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
json={
"plan_id": 3,
"customer_id": "12345678",
"amount": 1000,
"pin": "1234"
},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"success": true,
"message": "Betting wallet funded successfully.",
"pending": false,
"data": {
"reference": "SUB7E1BC10B4B3D1791190455",
"cashback_earned": 0.0,
"balance_before": 152830.5,
"balance_after": 151730.5
}
}{
"status": "error",
"success": false,
"message": "The provider could not complete this request. Your wallet has been refunded.",
"refunded": true,
"reference": "SUB7E1BC10B4B3D1791190455"
}Transaction status
GET https://sub.submefast.com/api/v1/receipt.php
Look up one of your transactions by reference — use it to follow a pending purchase until it is final, and to fetch tokens and PINs again.
Query parameters
| Field | Type | Description | |
|---|---|---|---|
reference | string (query) | required | The reference a purchase returned. |
status is pending or processing (not final yet — check again in a minute), success, or failed / reversed (your wallet was refunded).
curl -X GET 'https://sub.submefast.com/api/v1/receipt.php?reference=SUB7E1BC10B4B3D1791190455' \ -H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://sub.submefast.com/api/v1/receipt.php?reference=SUB7E1BC10B4B3D1791190455');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/receipt.php?reference=SUB7E1BC10B4B3D1791190455', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
},
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.get(
'https://sub.submefast.com/api/v1/receipt.php?reference=SUB7E1BC10B4B3D1791190455',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"success": true,
"data": {
"id": 9021,
"reference": "SUB7E1BC10B4B3D1791190455",
"transaction_ref": "SUB7E1BC10B4B3D1791190455",
"category": "electricity",
"type": "debit",
"amount": 5100.0,
"status": "success",
"description": "Ikeja Electric prepaid ₦5,000",
"phone": "",
"balance_before": 152830.5,
"balance_after": 147730.5,
"created_at": "2026-10-08 14:13:42",
"pins": [],
"token": "1234-5678-9012-3456-7890",
"meta": {
"meter": "45031234567"
}
}
}{
"status": "error",
"success": false,
"message": "Transaction not found."
}Transaction history
GET https://sub.submefast.com/api/v1/transactions.php
Your transactions, newest first.
Query parameters
| Field | Type | Description | |
|---|---|---|---|
page | integer (query) | optional | Page number. Default 1. |
per_page | integer (query) | optional | 10 to 50. Default 20. |
filter | string (query) | optional | One category: airtime, data, electricity, cabletv, exam_pin, recharge_card, data_card. |
curl -X GET 'https://sub.submefast.com/api/v1/transactions.php?page=1&per_page=20&filter=airtime' \ -H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://sub.submefast.com/api/v1/transactions.php?page=1&per_page=20&filter=airtime');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_KEY'),
],
]);
$res = json_decode(curl_exec($ch), true);
if ($res['status'] === 'success') print_r($res['data'] ?? $res);
elseif ($res['status'] === 'pending') echo 'Pending: ' . $res['data']['reference'];
else echo 'Error: ' . $res['message'];const res = await fetch('https://sub.submefast.com/api/v1/transactions.php?page=1&per_page=20&filter=airtime', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
},
});
const out = await res.json();
if (out.status === 'error') console.error(out.message);
else console.log(out);import os, requests
res = requests.get(
'https://sub.submefast.com/api/v1/transactions.php?page=1&per_page=20&filter=airtime',
headers={'Authorization': f"Bearer {os.environ['API_KEY']}"},
timeout=90,
)
out = res.json()
print(out['message'] if out['status'] == 'error' else out){
"status": "success",
"data": {
"data": [
{
"id": 9021,
"transaction_ref": "SUB7E1BC10B4B3D1791190455",
"type": "debit",
"category": "airtime",
"amount": "490.00",
"description": "₦500 MTN airtime to 08031234567",
"status": "success",
"created_at": "2026-10-08 14:13:42"
}
],
"total": 1,
"page": 1,
"per_page": 20,
"total_pages": 1
}
}