Click Airtime

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

HeaderRequiredDescription
Content-TypeYesMust be application/json
X-Click-Airtime-EmailYesYour registered account email address
X-Click-Airtime-TokenYesYour API authentication token

Body Parameters

ParameterTypeRequiredDescription
msisdnstringYesRecipient phone number in international format without + prefix (e.g., 263719545499)
amountnumberConditionalAmount to send in the destination currency (e.g., 100). Required unless using a FIXED_PRODUCT via productId.
extRefIdstringNoYour external reference ID for tracking and deduplication (e.g., 1W89-GZ881-WYYQ)
productIdnumberNoProduct 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

FieldTypeDescription
messagestringHuman-readable result description
statusCodenumberHTTP status code
codenumberResponse code (1 for success, see Response Codes for error codes)
data.transactionIdstringUnique Click Airtime transaction identifier (UUID)
data.statestringTransaction state: SUCCESS or FAILED
data.amountnumberAmount sent in destination currency
data.effectiveAmountnumberAmount deducted from your account balance (after rate conversion)
data.provider.transaction_idstringProvider's transaction ID
data.provider.reference_codestringProvider's reference code

Error Responses

HTTP StatusDescription
200Success (check code field — value 1 means success)
401Invalid or missing X-Click-Airtime-Email or X-Click-Airtime-Token headers
403Business logic failure (insufficient balance, invalid number, etc.) — check code and message
500Internal server error -- contact support if persistent

Important Notes

  • Phone numbers must be in international format without the + prefix (e.g., 263719545499 for Zimbabwe, 233541112259 for Ghana)
  • The amount is specified in the destination currency (the currency of the recipient's country)
  • Use a unique extRefId for 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.