Send Top-up
Send airtime top-up to a phone number using the Version 1 API.
Version 1 API (v1.4) -- This API is in maintenance mode and will not receive new features. New integrations should use the Version 2 API which provides richer transaction management, product catalogs, bundle support, and multiple payment methods.
POST /adp/topup
Send airtime to a mobile phone number. The amount is deducted from your account balance for the destination network.
Request
POST https://api.clickairtime.com/adp/topup
Content-Type: application/json
X-Click-Airtime-Email: your@email.com
X-Click-Airtime-Token: your-api-token
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
X-Click-Airtime-Email | Yes | Your registered account email address |
X-Click-Airtime-Token | Yes | Your API authentication token |
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
msisdn | string | Yes | Recipient phone number in international format without + prefix (e.g., 263719545499) |
amount | number | Conditional | Amount to send in the destination currency (e.g., 100). Required unless using a FIXED_PRODUCT via productId. |
extRefId | string | No | Your external reference ID for tracking and deduplication (e.g., 1W89-GZ881-WYYQ) |
productId | number | No | Product identifier from the Product Catalog. When provided, routes the top-up through the product's gateway configuration. |
Product-based top-ups: You can optionally specify a productId to use a pre-configured product. For FIXED_PRODUCT products, the amount is pre-determined and the amount field is ignored. For OPEN_PRODUCT products, you must still provide the amount. Browse available products via the Product Catalog endpoints.
Code Examples
curl -X POST https://api.clickairtime.com/adp/topup \
-H "Content-Type: application/json" \
-H "X-Click-Airtime-Email: your@email.com" \
-H "X-Click-Airtime-Token: your-api-token" \
-d '{
"msisdn": "263719545499",
"amount": 100,
"extRefId": "1W89-GZ881-WYYQ"
}'const response = await fetch('https://api.clickairtime.com/adp/topup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Click-Airtime-Email': 'your@email.com',
'X-Click-Airtime-Token': 'your-api-token',
},
body: JSON.stringify({
msisdn: '263719545499',
amount: 100,
extRefId: '1W89-GZ881-WYYQ',
}),
});
const result = await response.json();
if (result.code === 1) {
console.log('Transaction ID:', result.data.transactionId);
console.log('State:', result.data.state);
console.log('Amount:', result.data.amount);
} else {
console.error('Top-up failed:', result.message);
}import requests
response = requests.post(
'https://api.clickairtime.com/adp/topup',
headers={
'Content-Type': 'application/json',
'X-Click-Airtime-Email': 'your@email.com',
'X-Click-Airtime-Token': 'your-api-token',
},
json={
'msisdn': '263719545499',
'amount': 100,
'extRefId': '1W89-GZ881-WYYQ',
}
)
result = response.json()
if result['code'] == 1:
print(f"Transaction ID: {result['data']['transactionId']}")
print(f"State: {result['data']['state']}")
print(f"Amount: {result['data']['amount']}")
else:
print(f"Top-up failed: {result['message']}")$response = Http::withHeaders([
'Content-Type' => 'application/json',
'X-Click-Airtime-Email' => 'your@email.com',
'X-Click-Airtime-Token' => 'your-api-token',
])->post('https://api.clickairtime.com/adp/topup', [
'msisdn' => '263719545499',
'amount' => 100,
'extRefId' => '1W89-GZ881-WYYQ',
]);
$result = $response->json();
if ($result['code'] === 1) {
echo "Transaction ID: " . $result['data']['transactionId'];
echo "State: " . $result['data']['state'];
echo "Amount: " . $result['data']['amount'];
} else {
echo "Top-up failed: " . $result['message'];
}String body = """
{
"msisdn": "263719545499",
"amount": 100,
"extRefId": "1W89-GZ881-WYYQ"
}
""";
HttpResponse<String> response = Unirest
.post("https://api.clickairtime.com/adp/topup")
.header("Content-Type", "application/json")
.header("X-Click-Airtime-Email", "your@email.com")
.header("X-Click-Airtime-Token", "your-api-token")
.body(body)
.asString();
JSONObject result = new JSONObject(response.getBody());
if (result.getInt("code") == 1) {
JSONObject data = result.getJSONObject("data");
System.out.println("Transaction ID: " + data.getString("transactionId"));
System.out.println("State: " + data.getString("state"));
System.out.println("Amount: " + data.getDouble("amount"));
} else {
System.out.println("Top-up failed: " + result.getString("message"));
}Top-up with Product ID (Fixed Product)
curl -X POST https://api.clickairtime.com/adp/topup \
-H "Content-Type: application/json" \
-H "X-Click-Airtime-Email: your@email.com" \
-H "X-Click-Airtime-Token: your-api-token" \
-d '{
"msisdn": "233541112259",
"productId": 42,
"extRefId": "FIX-001-GHS5"
}'const response = await fetch('https://api.clickairtime.com/adp/topup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Click-Airtime-Email': 'your@email.com',
'X-Click-Airtime-Token': 'your-api-token',
},
body: JSON.stringify({
msisdn: '233541112259',
productId: 42,
extRefId: 'FIX-001-GHS5',
}),
});
const result = await response.json();
if (result.code === 1) {
console.log('Transaction ID:', result.data.transactionId);
console.log('Amount:', result.data.amount);
} else {
console.error('Top-up failed:', result.message);
}import requests
response = requests.post(
'https://api.clickairtime.com/adp/topup',
headers={
'Content-Type': 'application/json',
'X-Click-Airtime-Email': 'your@email.com',
'X-Click-Airtime-Token': 'your-api-token',
},
json={
'msisdn': '233541112259',
'productId': 42,
'extRefId': 'FIX-001-GHS5',
}
)
result = response.json()
if result['code'] == 1:
print(f"Transaction ID: {result['data']['transactionId']}")
print(f"Amount: {result['data']['amount']}")
else:
print(f"Top-up failed: {result['message']}")Response
Success (200)
{
"message": "Top-up successful! Airtime has been successfully topped up",
"statusCode": 200,
"code": 1,
"data": {
"transactionId": "f661f75a-c249-4aef-8cf3-abcdef123456",
"state": "SUCCESS",
"amount": 100,
"effectiveAmount": 100.00,
"provider": {
"transaction_id": "EXT-12345",
"reference_code": "REF-67890"
}
}
}
Failure -- Insufficient Balance
{
"message": "Request declined due to low balance",
"statusCode": 500,
"code": 20
}
Failure -- Invalid Recipient
{
"message": "Recipient's number is invalid",
"statusCode": 500,
"code": 30
}
Failure -- Duplicate External Reference
{
"message": "External reference ID already exists",
"statusCode": 500,
"code": 90
}
Error (401) -- Authentication Failed
{
"message": "Invalid or missing authentication credentials",
"statusCode": 401
}
Response Fields
| Field | Type | Description |
|---|---|---|
message | string | Human-readable result description |
statusCode | number | HTTP status code |
code | number | Response code (1 for success, see Response Codes for error codes) |
data.transactionId | string | Unique Click Airtime transaction identifier (UUID) |
data.state | string | Transaction state: SUCCESS or FAILED |
data.amount | number | Amount sent in destination currency |
data.effectiveAmount | number | Amount deducted from your account balance (after rate conversion) |
data.provider.transaction_id | string | Provider's transaction ID |
data.provider.reference_code | string | Provider's reference code |
Error Responses
| HTTP Status | Description |
|---|---|
| 200 | Success (check code field — value 1 means success) |
| 401 | Invalid or missing X-Click-Airtime-Email or X-Click-Airtime-Token headers |
| 403 | Business logic failure (insufficient balance, invalid number, etc.) — check code and message |
| 500 | Internal server error -- contact support if persistent |
Important Notes
- Phone numbers must be in international format without the
+prefix (e.g.,263719545499for Zimbabwe,233541112259for Ghana) - The
amountis specified in the destination currency (the currency of the recipient's country) - Use a unique
extRefIdfor each transaction to enable deduplication and easy lookup via the Transactions endpoint - Transactions are processed synchronously -- the response indicates the final status
- If the top-up fails at the network provider level, the balance is not deducted from your account
For the complete list of error codes and their meanings, see the Response Codes reference.
Migrating to V2? The Version 2 API uses a simplified flow with API key authentication and richer product selection. See the Migration Guide for a step-by-step walkthrough.
