Rate Limits
Understand Click Airtime API rate limits and implement proper throttling to avoid request rejections.
Rate limits protect the Click Airtime API from abuse and ensure fair access for all users. This guide explains the limits for each API version and how to handle rate-limited responses.
Rate Limit Overview
Version 2 API
| Endpoint Category | Rate Limit | Window |
|---|---|---|
Authentication (/enterprise/auth/*) | 20 requests | Per minute |
| Read operations (GET) | 300 requests | Per minute |
| Write operations (POST, PUT, DELETE) | 120 requests | Per minute |
| Transaction creation | 60 requests | Per minute |
| Catalog lookup | 120 requests | Per minute |
Version 1 API
| Endpoint | Rate Limit | Window |
|---|---|---|
POST /adp/topup | 60 requests | Per minute |
GET /adp/balances | 120 requests | Per minute |
GET /adp/transactions | 120 requests | Per minute |
Enterprise plans may have higher rate limits based on your subscription tier. Contact sales@clickairtime.com to discuss custom limits for high-volume integrations.
Rate Limit Headers
The V2 API includes rate limit information in the response headers:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1705312800
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum number of requests allowed in the current window |
X-RateLimit-Remaining | Number of requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the rate limit window resets |
Handling Rate Limits
When you exceed the rate limit, the API returns a 429 Too Many Requests response:
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Please retry after 45 seconds.",
"statusCode": 429
}
}
The response includes a Retry-After header with the number of seconds to wait.
Implementation
async function requestWithRateLimiting(fn) {
const response = await fn();
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10);
console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return fn(); // Retry once
}
return response;
}
// Proactive rate limiting using response headers
class RateLimitedClient {
constructor() {
this.remaining = Infinity;
this.resetAt = 0;
}
async request(fn) {
// Wait if we know we are out of requests
if (this.remaining <= 0) {
const waitMs = Math.max(0, (this.resetAt * 1000) - Date.now());
if (waitMs > 0) {
console.log(`Rate limit reached. Waiting ${Math.ceil(waitMs / 1000)}s...`);
await new Promise(resolve => setTimeout(resolve, waitMs));
}
}
const response = await fn();
// Update rate limit state from headers
this.remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || 'Infinity', 10);
this.resetAt = parseInt(response.headers.get('X-RateLimit-Reset') || '0', 10);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return fn();
}
return response;
}
}import time
import requests as http
class RateLimitedClient:
def __init__(self):
self.remaining = float('inf')
self.reset_at = 0
def request(self, method, url, **kwargs):
# Wait if we know we are out of requests
if self.remaining <= 0:
wait_seconds = max(0, self.reset_at - time.time())
if wait_seconds > 0:
print(f"Rate limit reached. Waiting {int(wait_seconds)}s...")
time.sleep(wait_seconds)
response = http.request(method, url, **kwargs)
# Update rate limit state from headers
self.remaining = int(response.headers.get('X-RateLimit-Remaining', float('inf')))
self.reset_at = int(response.headers.get('X-RateLimit-Reset', 0))
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Retrying after {retry_after} seconds...")
time.sleep(retry_after)
response = http.request(method, url, **kwargs)
return response
# Usage
client = RateLimitedClient()
response = client.request(
'GET',
'https://api.clickairtime.com/enterprise/wallets',
headers={
'Authorization': f'Bearer {access_token}',
'X-Company-ID': str(company_id),
}
)Best Practices
-
Monitor rate limit headers -- Track
X-RateLimit-Remainingproactively to avoid hitting the limit. -
Implement queuing -- For high-volume applications, use a job queue to throttle API calls to stay within limits.
-
Cache where possible -- Cache catalog lookups and balance checks to reduce unnecessary API calls.
-
Spread requests evenly -- Instead of bursting 60 requests at once, spread them across the minute window (1 request per second).
-
Handle 429 gracefully -- Always respect the
Retry-Afterheader and do not immediately retry rate-limited requests. -
Use separate rate limit budgets -- Track rate limits independently for each endpoint category to maximize throughput.
