Rate Limits
Understanding and managing API rate limits to ensure smooth operation.
Default Rate Limits
The API enforces the following default rate limits:
| Category | Limit | Window | Applies To |
|---|---|---|---|
| General API | 100 requests | per minute | All endpoints |
| Message Sending | 30 messages | per minute | Per session |
| Session Creation | 10 sessions | per hour | Per API key |
| QR Code Polling | 1 request | per 5 seconds | Per session |
Rate Limit Headers
Every API response includes rate limit information in the headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1706774460
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | Unix timestamp when the limit resets |
Rate Limit Response
When you exceed a rate limit, you'll receive a 429 Too Many Requests response:
{
"success": false,
"error": "Rate limit exceeded. Please wait 45 seconds before trying again.",
"retryAfter": 45
}
Managed API Keys
Admin-created API keys can have custom rate limits:
Standard Key Example
{
"rateLimits": {
"general": 100,
"messages": 30,
"sessions": 10
}
}
Premium Key Example
{
"rateLimits": {
"general": 500,
"messages": 100,
"sessions": 50
}
}
Trial Key Example
{
"rateLimits": {
"general": 50,
"messages": 10,
"sessions": 2
}
}
Best Practices
1. Implement Exponential Backoff
async function sendWithBackoff(whatsapp, to, message, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await whatsapp.sendText(to, message);
} catch (error) {
if (error.message.includes('Rate limit') && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
}
2. Use Message Queues
Instead of sending messages immediately, queue them:
from queue import Queue
import time
message_queue = Queue()
def queue_message(to, message):
message_queue.put({'to': to, 'message': message})
def process_queue(whatsapp, delay=2):
while not message_queue.empty():
item = message_queue.get()
whatsapp.send_text(item['to'], item['message'])
time.sleep(delay) # Respect rate limits
3. Monitor Rate Limit Headers
<?php
function sendWithRateLimitCheck($whatsapp, $to, $message) {
$response = $whatsapp->sendText($to, $message);
// Check remaining requests
$remaining = $response['headers']['X-RateLimit-Remaining'] ?? null;
if ($remaining !== null && $remaining < 5) {
// Close to limit, add delay
sleep(2);
}
return $response;
}
4. Batch Operations
Group operations to minimize API calls:
// Instead of multiple individual calls
for (const recipient of recipients) {
await whatsapp.sendText(recipient, message);
}
// Use broadcast lists
await whatsapp.createBroadcast('Marketing List', recipients);
await whatsapp.sendToBroadcast(broadcastId, message);
5. Cache Data
Cache frequently accessed data to reduce API calls:
import time
class CachedWhatsApp:
def __init__(self, whatsapp):
self.whatsapp = whatsapp
self.contacts_cache = None
self.cache_time = 0
self.cache_ttl = 300 # 5 minutes
def get_contacts(self):
now = time.time()
if self.contacts_cache and (now - self.cache_time) < self.cache_ttl:
return self.contacts_cache
self.contacts_cache = self.whatsapp.get_contacts()
self.cache_time = now
return self.contacts_cache
Handling Rate Limits
Detect Rate Limit Errors
function isRateLimitError(error) {
return error.response?.status === 429 ||
error.message.includes('Rate limit');
}
try {
await whatsapp.sendText(to, message);
} catch (error) {
if (isRateLimitError(error)) {
// Handle rate limit
const retryAfter = error.response?.data?.retryAfter || 60;
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
} else {
throw error;
}
}
Automatic Retry with Delay
<?php
function sendWithRetry($whatsapp, $to, $message, $maxRetries = 3) {
$attempt = 0;
while ($attempt < $maxRetries) {
try {
return $whatsapp->sendText($to, $message);
} catch (Exception $e) {
if (strpos($e->getMessage(), 'Rate limit') !== false) {
$attempt++;
if ($attempt >= $maxRetries) {
throw $e;
}
// Wait before retry
sleep(pow(2, $attempt));
} else {
throw $e;
}
}
}
}
Rate Limit Strategies
Strategy 1: Token Bucket
import time
class TokenBucket:
def __init__(self, capacity, refill_rate):
self.capacity = capacity
self.tokens = capacity
self.refill_rate = refill_rate
self.last_refill = time.time()
def consume(self, tokens=1):
self._refill()
if self.tokens >= tokens:
self.tokens -= tokens
return True
return False
def _refill(self):
now = time.time()
elapsed = now - self.last_refill
refill = elapsed * self.refill_rate
self.tokens = min(self.capacity, self.tokens + refill)
self.last_refill = now
def wait_time(self, tokens=1):
if self.tokens >= tokens:
return 0
needed = tokens - self.tokens
return needed / self.refill_rate
# Usage
bucket = TokenBucket(capacity=30, refill_rate=0.5) # 30 per minute
def send_with_bucket(whatsapp, to, message):
wait = bucket.wait_time()
if wait > 0:
time.sleep(wait)
bucket.consume()
return whatsapp.send_text(to, message)
Strategy 2: Sliding Window
class SlidingWindow {
constructor(limit, windowMs) {
this.limit = limit;
this.windowMs = windowMs;
this.requests = [];
}
canMakeRequest() {
const now = Date.now();
const windowStart = now - this.windowMs;
// Remove old requests
this.requests = this.requests.filter(time => time > windowStart);
return this.requests.length < this.limit;
}
recordRequest() {
this.requests.push(Date.now());
}
async waitForSlot() {
while (!this.canMakeRequest()) {
await new Promise(resolve => setTimeout(resolve, 100));
}
this.recordRequest();
}
}
// Usage
const limiter = new SlidingWindow(30, 60000); // 30 per minute
async function sendWithLimiter(whatsapp, to, message) {
await limiter.waitForSlot();
return whatsapp.sendText(to, message);
}
Upgrading Rate Limits
To increase your rate limits:
- Contact Admin - Request higher limits for your API key
- Upgrade Plan - Move from trial to standard or premium
- Multiple Sessions - Distribute load across multiple sessions
Request Rate Limit Increase
# Admin updates rate limits
curl -X PUT http://localhost:3000/admin/api-keys/{key_id}/rate-limits \
-H "X-API-Key: wamk_admin_key" \
-H "Content-Type: application/json" \
-d '{
"general": 500,
"messages": 100,
"sessions": 50
}'
Monitoring Rate Limits
Track Usage
class RateLimitMonitor {
constructor() {
this.stats = {
requests: 0,
rateLimited: 0,
lastReset: Date.now()
};
}
recordRequest(response) {
this.stats.requests++;
const remaining = response.headers['x-ratelimit-remaining'];
const limit = response.headers['x-ratelimit-limit'];
console.log(`Rate Limit: ${remaining}/${limit}`);
if (remaining < limit * 0.1) {
console.warn('⚠️ Approaching rate limit!');
}
}
recordRateLimit() {
this.stats.rateLimited++;
console.error('❌ Rate limit exceeded!');
}
getStats() {
return this.stats;
}
}
WhatsApp-Specific Limits
Beyond API rate limits, WhatsApp itself has restrictions:
Message Limits
- New Numbers: ~50-100 messages per day initially
- Established Numbers: Higher limits after building reputation
- Bulk Messaging: Avoid sending identical messages to many recipients
Best Practices for WhatsApp
- Warm Up New Numbers - Start slow, increase gradually
- Vary Messages - Don't send identical text to everyone
- User Engagement - Higher engagement = better limits
- Avoid Spam - Don't message users who haven't opted in
- Response Rate - Maintain good response rates
Warning Signs
Watch for these indicators of potential issues:
- Messages taking longer to send
- Increased delivery failures
- "This account is not allowed to use WhatsApp" errors
- Temporary bans (24-48 hours)
Summary
- ✅ Implement exponential backoff
- ✅ Use message queues
- ✅ Monitor rate limit headers
- ✅ Cache frequently accessed data
- ✅ Respect WhatsApp's limits
- ✅ Start slow with new numbers
- ✅ Track usage and errors
- ✅ Request limit increases when needed