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

MethodPathDescriptionBody fields
GET/balanceYour wallet balance.
GET/servicesService catalogue with your prices. Optional query: category (airtime, data, tv, electricity, education, epin, bet).
POST/airtimeBuy airtime.network, amount, phone, reference
POST/dataBuy a data plan.service_id, phone, reference
POST/cable/verifyCheck a smartcard or IUC number.service_id, smartcard
POST/cablePay a cable TV package.service_id, smartcard, reference
POST/electricity/verifyCheck a meter number.service_id, meter_number, meter_type (prepaid or postpaid)
POST/electricityBuy electricity.service_id, meter_number, meter_type, amount, reference
POST/betting/verifyCheck a betting account.service_id, customer_id
POST/bettingFund a betting wallet.service_id, customer_id, amount, reference
POST/exam-pinsBuy exam pins.service_id, quantity, reference
POST/recharge-pinsBuy recharge card PINs.service_id, quantity, reference
GET/transactionStatus 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

HTTPMeaning
401Missing or wrong key or secret
403IP not allowed, or account not cleared for the API
404Unknown endpoint or transaction
422Invalid input, unavailable service, or insufficient wallet balance (see message)
429Rate limit (120 requests per minute per key)
5xxSomething went wrong on our side. Check the order with /transaction before retrying.