Skip to main content

Rate Limits

Understanding and managing API rate limits to ensure smooth operation.

Default Rate Limits

The API enforces the following default rate limits:

CategoryLimitWindowApplies To
General API100 requestsper minuteAll endpoints
Message Sending30 messagesper minutePer session
Session Creation10 sessionsper hourPer API key
QR Code Polling1 requestper 5 secondsPer 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
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRemaining requests in current window
X-RateLimit-ResetUnix 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:

  1. Contact Admin - Request higher limits for your API key
  2. Upgrade Plan - Move from trial to standard or premium
  3. 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

  1. Warm Up New Numbers - Start slow, increase gradually
  2. Vary Messages - Don't send identical text to everyone
  3. User Engagement - Higher engagement = better limits
  4. Avoid Spam - Don't message users who haven't opted in
  5. 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