WhatsApp API Rate Limits: How to Avoid Blocks and Scale
Complete guide on WhatsApp Business API message limits, quality tiers and how to avoid blocks in 2025
Understanding Rate Limits
As of 2025, Meta changed how message limits work. Limits are now applied at the Business Portfolio level, not per individual phone number.
Types of Limits
1. Conversation Limit per 24h
This is the main limit. It counts how many new conversations you can start in a 24-hour window.
| Tier | Conversations/24h Limit | |------|------------------------| | Unverified | 250 | | Tier 1 | 1,000 | | Tier 2 | 10,000 | | Tier 3 | 100,000 | | Tier 4 | Unlimited |
2. API Request Limits
Beyond conversations, there are limits on API call frequency:
- Messages: ~80 per second per number
- Templates: ~60 per second
- Media upload: ~30 per second
Error 131056: Rate Limit Hit
{
"error": {
"message": "(#131056) Rate limit hit",
"type": "OAuthException",
"code": 131056,
"error_subcode": 2494055,
"fbtrace_id": "..."
}
}
Common Causes
- Too many conversations started in 24h
- Too many messages to the same number
- Too many API requests in a short period
Solution: Implement Rate Limiting
import Bottleneck from 'bottleneck';
// Limiter for API requests
const apiLimiter = new Bottleneck({
minTime: 50, // 50ms between requests = 20/second
maxConcurrent: 5,
reservoir: 1000, // 1000 requests
reservoirRefreshAmount: 1000,
reservoirRefreshInterval: 60 * 1000 // Refresh every minute
});
// Limiter for new conversations
const conversationLimiter = new Bottleneck({
reservoir: 200, // Safety buffer below the limit
reservoirRefreshAmount: 200,
reservoirRefreshInterval: 24 * 60 * 60 * 1000 // Refresh every 24h
});
async function sendMessage(to, message) {
return apiLimiter.schedule(() =>
conversationLimiter.schedule(() =>
sendWhatsAppMessage(to, message)
)
);
}
How to Upgrade Tiers
Requirements for Upgrade
- Business Verification: Complete verification in Meta Business Suite
- Account Quality: Maintain "Connected" or "High" status
- Consistent Volume: Send messages regularly without issues
Automatic Process
Meta upgrades your tier automatically when:
- You send 2x your current tier limit in 7 days
- Your quality remains high
- There aren't many blocks/reports
// Monitor usage to know when close to upgrade
async function checkUsageStats() {
const response = await fetch(
`https://graph.facebook.com/v18.0/${wabaId}?fields=analytics.start(${startDate}).end(${endDate}).granularity(DAY)`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
const data = await response.json();
return data.analytics;
}
Quality Rating: The Key to Scaling
Quality Status
| Status | Meaning | Action | |--------|---------|--------| | 🟢 High | Excellent | Keep it up | | 🟡 Medium | Warning | Review your messages | | 🔴 Low | Problem | Stop and fix | | ⚫ Flagged | Critical | Risk of suspension |
What Affects Quality
Negative:
- Users blocking your number
- Spam reports
- "Don't want to receive" responses
- Rejected templates
Positive:
- Users replying
- Long conversations
- Few reports
- Templates approved on first try
Monitor Quality via API
async function checkQualityRating() {
const response = await fetch(
`https://graph.facebook.com/v18.0/${phoneNumberId}?fields=quality_rating,messaging_limit_tier`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
const data = await response.json();
console.log('Quality:', data.quality_rating);
console.log('Current tier:', data.messaging_limit_tier);
return data;
}
Strategies to Avoid Blocks
1. Gradual Warm-up
Don't send at maximum limit right away:
class MessageWarmup {
constructor(dailyLimit) {
this.dailyLimit = dailyLimit;
this.warmupDays = 14;
this.currentDay = 0;
}
getDailyAllowance() {
// Start with 10% and grow to 100% in 14 days
const percentage = Math.min(
0.1 + (this.currentDay * 0.9 / this.warmupDays),
1
);
return Math.floor(this.dailyLimit * percentage);
}
incrementDay() {
this.currentDay++;
}
}
// Usage
const warmup = new MessageWarmup(1000);
console.log('Day 1:', warmup.getDailyAllowance()); // ~100
warmup.incrementDay();
console.log('Day 2:', warmup.getDailyAllowance()); // ~164
// ...continues growing
2. Exponential Backoff
When receiving rate limit, wait before retrying:
async function sendWithBackoff(to, message, attempt = 1) {
try {
return await sendMessage(to, message);
} catch (error) {
if (error.code === 131056 && attempt <= 5) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s, 16s, 32s
console.log(`Rate limit. Waiting ${delay/1000}s...`);
await new Promise(r => setTimeout(r, delay));
return sendWithBackoff(to, message, attempt + 1);
}
throw error;
}
}
3. Priority Queue
import { Queue, Worker } from 'bullmq';
const messageQueue = new Queue('whatsapp-messages');
// Add message to queue
async function queueMessage(to, message, priority = 'normal') {
await messageQueue.add(
'send',
{ to, message },
{
priority: priority === 'urgent' ? 1 : 10,
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000
}
}
);
}
// Worker processes respecting rate limits
const worker = new Worker('whatsapp-messages', async job => {
await sendMessage(job.data.to, job.data.message);
}, {
limiter: {
max: 50, // 50 messages
duration: 1000 // per second
}
});
4. Smart Segmentation
Don't send to your entire base at once:
async function sendCampaign(recipients, template) {
// Divide into batches
const batchSize = 100;
const batches = [];
for (let i = 0; i < recipients.length; i += batchSize) {
batches.push(recipients.slice(i, i + batchSize));
}
// Send with interval between batches
for (const batch of batches) {
await Promise.all(
batch.map(r => queueMessage(r.phone, template))
);
// Wait 1 minute between batches
await new Promise(r => setTimeout(r, 60000));
// Check quality
const quality = await checkQualityRating();
if (quality.quality_rating === 'LOW') {
console.log('Low quality! Pausing campaign.');
break;
}
}
}
Per-Recipient Limits
Beyond general limits, there's a limit on messages per recipient:
- Maximum of ~256 template messages per conversation
- If user doesn't reply, space out messages
// Track messages per user
const userMessageCount = new Map();
async function sendToUser(userId, message) {
const count = userMessageCount.get(userId) || 0;
if (count >= 3 && !hasUserReplied(userId)) {
// User hasn't replied after 3 messages
console.log('Too many messages without response. Skipping.');
return;
}
await sendMessage(userId, message);
userMessageCount.set(userId, count + 1);
}
Monitoring Dashboard
class WhatsAppMonitor {
constructor() {
this.metrics = {
sent: 0,
delivered: 0,
read: 0,
failed: 0,
rateLimited: 0
};
}
recordSent() { this.metrics.sent++; }
recordDelivered() { this.metrics.delivered++; }
recordFailed() { this.metrics.failed++; }
recordRateLimited() { this.metrics.rateLimited++; }
getStats() {
return {
...this.metrics,
deliveryRate: this.metrics.sent > 0
? (this.metrics.delivered / this.metrics.sent * 100).toFixed(2) + '%'
: '0%',
rateLimitRate: this.metrics.sent > 0
? (this.metrics.rateLimited / this.metrics.sent * 100).toFixed(2) + '%'
: '0%'
};
}
shouldPause() {
// Pause if more than 5% of messages are getting rate limited
const rate = this.metrics.rateLimited / this.metrics.sent;
return rate > 0.05;
}
}
Conclusion
To scale with WhatsApp API without blocks:
- Start slow - Do gradual warm-up
- Monitor quality - Stop if it drops
- Use queues - Control volume
- Implement backoff - Respect limits
- Segment campaigns - Don't send everything at once
Want help scaling your WhatsApp operation? Contact me for specialized consulting.