Updated on October 28, 2026
The WhatsApp Cloud API supports, by default, up to 80 messages per second (mps) per registered phone number, counting inbound and outbound traffic, and eligible numbers are upgraded automatically to up to 1,000 mps. Go above that pace and the API returns error 130429. Error 131056 is a different thing: it is the pair rate limit, triggered when you send too many messages from the same number to the same recipient in a short time.
This article explains how throughput, the pair rate limit, API call limits and the 24-hour messaging limit differ, which error codes each one returns, and shows a send queue with backoff in Node.js and Python.
Four limits that get mixed up
Developers new to the WhatsApp Business API tend to call everything a “rate limit”. In Meta’s documentation these are separate limits, with different causes and fixes:
| Limit | What it measures | Error | What to do |
|---|---|---|---|
| Throughput | Messages per second for one phone number | 130429 | Slow down and retry |
| Pair rate limit | Messages from your number to the same user in a short window | 131056 | Wait before messaging that user again |
| API call limits | Requests from the app or account to the API | 4 (app) and 80007 (WhatsApp Business account) | Reduce call frequency |
| Messaging limit | Unique recipients outside the customer service window in 24 hours, per business portfolio | Sends rejected above the limit | Move up tiers through quality and volume |
The last one is about daily reach, not speed; see WhatsApp phone number quality rating for how quality affects it. This article focuses on the first three.
Throughput: messages per second
According to Meta’s throughput page:
- Each phone number registered on Cloud API supports up to 80 mps by default.
- Throughput counts inbound and outbound messages of all types. A number that receives a lot of messages has less headroom to send.
- WhatsApp Business app numbers connected to the API (coexistence) have a fixed throughput of 20 mps.
- Above the limit, the API returns
130429until you are back within your allowed level.
Automatic upgrade to 1,000 mps
A number is upgraded automatically to up to 1,000 mps, free of charge and with no pricing change, when it meets all three conditions:
- The business portfolio has an unlimited messaging limit.
- The number has messaged 100,000 or more unique WhatsApp user numbers outside a customer service window within a moving 24-hour period.
- The number’s
quality_scoreis yellow (medium quality) or higher.
During the upgrade, which can take up to 1 minute, the number is unavailable and the API returns error 131057. When it finishes, a phone_number_quality_update webhook arrives with event set to THROUGHPUT_UPGRADE. To check a number’s current level:
curl "https://graph.facebook.com/v26.0/PHONE_NUMBER_ID?fields=throughput" \
-H "Authorization: Bearer $TOKEN"
The same page gives two practical tips for high-volume senders. First, your webhook server should handle roughly 3x your outgoing message traffic (each sent message produces status events) plus 1x your expected incoming traffic, with median latency under 250 ms and fewer than 1% of requests above 1 second. Second, for media messages, use media IDs from a prior upload instead of links to files hosted on your own servers, to get the full benefit of higher throughput.
Want to try it while you read? Create a Vectalk account and send your first message through the official API. Create free account →
Pair rate limit and error 131056
Throughput is designed for campaigns that reach many different users. If you send many messages to the same user, you hit a different limit. Meta’s error code table describes 131056 as too many messages sent from the sender phone number to the same recipient phone number in a short period of time. The suggested fix is to wait and retry, if you really intend to send more messages to that number.
Meta does not publish an exact figure for the pair rate limit in that documentation, so do not code against a fixed value. Typical scenarios that trigger 131056:
- A bot that splits one long answer into ten consecutive messages.
- An integration stuck in a loop resending the same notification.
- Load tests aimed at a single test phone number.
- A broadcast with a duplicated list, sending the same template twice to every contact.
The fix is almost always design, not infrastructure: merge content into one message, deduplicate the list, and space out sends to the same contact. For load tests, spread sends across several test recipients.
Call errors: 4, 80007 and 131048
On top of message limits, the API also limits calls:
4: the app has reached its API call rate limit. Meta suggests checking the app’s rate limit section in the App Dashboard and reducing the frequency or number of queries.80007: the WhatsApp Business account has reached its rate limit. Same fix: try later and reduce frequency.131048: not about speed. It means the number has sending restrictions due to quality; Meta’s advice is to check quality status in WhatsApp Manager. Retrying only makes it worse.
Query errors (4 and 80007) are common in integrations that poll, such as checking template or phone number status every few seconds. Use webhooks instead; they tell you when something changes.
Queue with backoff: the recommended design
To respect all of these limits at once, the simplest design is a queue with three rules:
- Global pace per number: a limiter that releases at most N sends per second, with N below the number’s throughput (for example 60 for a standard 80 mps number, leaving room for inbound messages).
- Retry with exponential backoff and jitter for
130429,4and80007: wait 1 s, 2 s, 4 s, 8 s… with some randomness, up to a cap. - Per-recipient deferral for
131056: the send goes back into that contact’s queue with a delay, without blocking everyone else.
Non-transient errors such as 131048 should not be retried. Stop, log and investigate.
Node.js example
const GRAPH = 'https://graph.facebook.com/v26.0';
const GLOBAL_RETRY = new Set([130429, 4, 80007]);
const MPS = 60; // below the default 80 mps
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function send(phoneNumberId, token, payload) {
const res = await fetch(`${GRAPH}/${phoneNumberId}/messages`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const json = await res.json();
if (res.ok) return { ok: true, id: json.messages?.[0]?.id };
return { ok: false, code: json.error?.code };
}
export async function processQueue(queue, phoneNumberId, token) {
const interval = 1000 / MPS;
while (queue.length) {
const item = queue.shift();
if (item.notBefore && item.notBefore > Date.now()) {
queue.push(item); // not this contact's turn yet
await sleep(interval);
continue;
}
const r = await send(phoneNumberId, token, item.payload);
if (r.ok) {
item.onSuccess?.(r.id);
} else if (GLOBAL_RETRY.has(r.code) && (item.attempts ?? 0) < 6) {
item.attempts = (item.attempts || 0) + 1;
const wait = Math.min(2 ** item.attempts * 1000, 60000) + Math.random() * 500;
await sleep(wait); // hold the whole number
queue.unshift(item);
} else if (r.code === 131056) {
item.notBefore = Date.now() + 30000; // defer this recipient only
queue.push(item);
} else {
item.onFailure?.(r.code); // 131048 and others: do not retry
}
await sleep(interval);
}
}
The 30-second delay for 131056 is a starting point, not a Meta figure: tune it from your own logs. In production, replace the array with a persistent queue (Redis, SQS, RabbitMQ) so sends survive a process crash.
Python example
import asyncio
import random
import httpx
GRAPH = "https://graph.facebook.com/v26.0"
GLOBAL_RETRY = {130429, 4, 80007}
MPS = 60
async def send(client, phone_number_id, payload, attempts=0):
r = await client.post(f"{GRAPH}/{phone_number_id}/messages", json=payload)
if r.status_code == 200:
return r.json()["messages"][0]["id"]
code = r.json().get("error", {}).get("code")
if code in GLOBAL_RETRY and attempts < 6:
await asyncio.sleep(min(2 ** attempts, 60) + random.random() / 2)
return await send(client, phone_number_id, payload, attempts + 1)
if code == 131056:
await asyncio.sleep(30) # defer this recipient only
return await send(client, phone_number_id, payload, attempts + 1)
raise RuntimeError(f"permanent failure: {code}")
async def broadcast(phone_number_id, token, payloads):
headers = {"Authorization": f"Bearer {token}"}
async with httpx.AsyncClient(headers=headers, timeout=30) as client:
tasks = []
for p in payloads:
tasks.append(asyncio.create_task(send(client, phone_number_id, p)))
await asyncio.sleep(1 / MPS) # global pace
return await asyncio.gather(*tasks, return_exceptions=True)
A 200 response only means the message was accepted. Later failures arrive in the status webhook with status: "failed" and the code under errors; handle the same codes in both places. See WhatsApp API message status webhooks for the format, and WhatsApp API error codes for the rest of the list.
Checklist before a large send
- Know the number’s throughput (
?fields=throughput) and set your limiter below it. - If the number uses coexistence, plan for 20 mps.
- Deduplicate the list so no contact gets the same message twice.
- Make sure the volume fits your 24-hour messaging limit.
- Size the webhook for roughly 3x your outgoing traffic.
- Upload media in advance and send by ID.
- Log error codes so you can tune the pace with real data.
How to do this with Vectalk
Vectalk is a proxy for Meta’s official Cloud API, so the limits in this article are Meta’s own: 80 mps per number by default, 20 mps with coexistence, and the same error codes, returned in the response body and in webhooks in the official format. Sends go through POST https://apis.vectalk.com.br/api/messages, and the number’s throughput level shows up in the GET /phone-numbers response of the Vectalk API. The queue and backoff logic shown here works unchanged. Sign up at apps.vectalk.com.br/register or read the quickstart.
If you are still building the foundation of your integration, see how to validate the webhook signature and how to register the phone number on Cloud API. The big picture is in our WhatsApp Cloud API guide.
Frequently asked questions
How many messages per second can the WhatsApp API send?
Up to 80 per second per registered number by default, counting inbound and outbound. Eligible numbers are upgraded automatically to up to 1,000 mps. Numbers in coexistence with the WhatsApp Business app are fixed at 20 mps.
What is the difference between error 130429 and 131056?
130429 is throughput: the number exceeded its total allowed pace, regardless of recipient. 131056 is the pair rate limit: too many messages to the same recipient in a short time. The first calls for a slower global pace; the second for spacing out sends to that contact.
Do multiple phone numbers increase speed?
Throughput is per number, so several numbers add up in speed. But the 24-hour unique-recipient limit belongs to the business portfolio and is shared by all of its numbers, and each number’s quality is still assessed. More numbers do not fix a reach limit.
Am I charged for a message rejected by a rate limit?
Meta charges for delivered messages. A message rejected with 130429 or 131056 never reaches the recipient and should not be billed; always reconcile against status webhooks.
How do I know if my number already has 1,000 mps?
Call GET /PHONE_NUMBER_ID?fields=throughput. When the upgrade happens, Meta also sends a phone_number_quality_update webhook with the THROUGHPUT_UPGRADE event.
Official sources consulted on October 28, 2026: Throughput (Meta), Cloud API error codes (Meta), Messaging limits (Meta), Phone numbers in the Vectalk API.
Try the official WhatsApp API today
Create your account, generate a token and send your first message with the quick-start guide. Same payloads and webhooks as Meta's Cloud API.
