Developer API
Sell airtime, data, TV, electricity, betting funding, exam pins and recharge PINs from your own app. API access is for verified users. Open your API dashboard.
Base URL
https://vickyconnect.com/api/v1
Authentication
Send both values with every request, from your server only. Never put them in a mobile app or web page.
Authorization: Bearer YOUR_API_KEY X-API-Secret: YOUR_SECRET
Sandbox keys (names start with vt_test_) simulate every purchase and never spend money. Live keys start with vt_live_. You can lock a live key to your server IP addresses in the dashboard.
Responses
Every response is JSON with the same shape. status is success or error.
{
"status": "success",
"message": "Successful",
"data": {
"reference": "VTU260101120000AB12CD34",
"client_reference": "order-0001-abc",
"service": "airtime",
"product": "MTN airtime ₦100.00",
"recipient": "08012345678",
"amount": "98.60",
"status": "successful",
"created_at": "2026-01-01 12:00:00"
}
}
Amounts are in naira and use your own pricing tier. The amount field is what was taken from your wallet.
Endpoints
| Method | Path | Description | Body fields |
|---|---|---|---|
| GET | /balance | Your wallet balance. | |
| GET | /services | Service catalogue with your prices. Optional query: category (airtime, data, tv, electricity, education, epin, bet). | |
| POST | /airtime | Buy airtime. | network, amount, phone, reference |
| POST | /data | Buy a data plan. | service_id, phone, reference |
| POST | /cable/verify | Check a smartcard or IUC number. | service_id, smartcard |
| POST | /cable | Pay a cable TV package. | service_id, smartcard, reference |
| POST | /electricity/verify | Check a meter number. | service_id, meter_number, meter_type (prepaid or postpaid) |
| POST | /electricity | Buy electricity. | service_id, meter_number, meter_type, amount, reference |
| POST | /betting/verify | Check a betting account. | service_id, customer_id |
| POST | /betting | Fund a betting wallet. | service_id, customer_id, amount, reference |
| POST | /exam-pins | Buy exam pins. | service_id, quantity, reference |
| POST | /recharge-pins | Buy recharge card PINs. | service_id, quantity, reference |
| GET | /transaction | Status of an order. Query: reference or client_reference. Pins and tokens are included when ready. |
service_id values come from the /services endpoint and stay the same even when the upstream provider changes its own codes. Airtime uses the network code (for example mtn) listed in /services.
reference is your own unique id for each purchase (8 to 64 letters, numbers, dash or underscore). Sending the same reference again never charges twice, it returns the first result.
Example: buy airtime
PHP (cURL)
<?php
$ch = curl_init('https://vickyconnect.com/api/v1/airtime');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'X-API-Secret: YOUR_SECRET',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'network' => 'mtn', 'amount' => 100,
'phone' => '08012345678', 'reference' => 'order-0001-abc',
]),
]);
echo curl_exec($ch);JavaScript (fetch)
const res = await fetch('https://vickyconnect.com/api/v1/airtime', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'X-API-Secret': 'YOUR_SECRET',
'Content-Type': 'application/json'
},
body: JSON.stringify({ network: 'mtn', amount: 100, phone: '08012345678', reference: 'order-0001-abc' })
});
console.log(await res.json());Node.js
// Node.js 18 or newer (built in fetch). Run this on your server, never in a browser.
const response = await fetch('https://vickyconnect.com/api/v1/airtime', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
'X-API-Secret': process.env.VT_API_SECRET,
'Content-Type': 'application/json',
},
body: JSON.stringify({ network: 'mtn', amount: 100, phone: '08012345678', reference: 'order-0001-abc' }),
});
console.log(await response.json());Python (requests)
import requests
r = requests.post(
"https://vickyconnect.com/api/v1/airtime",
headers={"Authorization": "Bearer YOUR_API_KEY", "X-API-Secret": "YOUR_SECRET"},
json={"network": "mtn", "amount": 100, "phone": "08012345678", "reference": "order-0001-abc"},
timeout=40,
)
print(r.json())Order status
A purchase can be successful, pending or failed. Failed orders are refunded to your wallet automatically. A pending order is still being processed: wait for the webhook or call /transaction. Do not retry with a new reference unless the order is failed.
Webhooks
Set a webhook URL in your dashboard. We send a POST with a JSON body for transaction.successful, transaction.failed and transaction.pending. Each event has a unique event_id, so handle every event once. We retry failed deliveries up to 5 times. Pins and tokens are never sent in webhooks, fetch them from /transaction.
Headers: X-VickyConnect-Event, X-VickyConnect-Event-Id, X-VickyConnect-Timestamp, X-VickyConnect-Signature (HMAC SHA256 of timestamp.body with your signing secret). Your URL must use https and a public address.
Verify in PHP
<?php
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_VICKYCONNECT_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_VICKYCONNECT_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $ts . '.' . $raw, 'YOUR_WEBHOOK_SIGNING_SECRET');
if (!hash_equals($expected, $sig) || abs(time() - (int)$ts) > 300) {
http_response_code(401);
exit;
}
$event = json_decode($raw, true); // handle once per event_id
http_response_code(200);Verify in Node.js
const crypto = require('crypto');
// Use the raw request body (not a re-serialised object)
function valid(rawBody, ts, sig, secret) {
const expected = crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig)) && Math.abs(Date.now() / 1000 - Number(ts)) < 300;
}Errors
| HTTP | Meaning |
|---|---|
| 401 | Missing or wrong key or secret |
| 403 | IP not allowed, or account not cleared for the API |
| 404 | Unknown endpoint or transaction |
| 422 | Invalid input, unavailable service, or insufficient wallet balance (see message) |
| 429 | Rate limit (120 requests per minute per key) |
| 5xx | Something went wrong on our side. Check the order with /transaction before retrying. |