# WhatsApp error 130429: Rate limit hit — cause and fix

URL: https://wa.genuka.com/en/docs/errors/130429
Language: English

> WhatsApp error 130429 (Rate limit hit) means your number exceeded its Cloud API throughput. The per-number caps, and how to retry without losing messages.

WhatsApp error 130429 means your number went over its sending throughput on Meta's Cloud API:
80 messages per second by default, 20 for a number in coexistence with the WhatsApp Business app.
Nothing is broken. Slow down, retry with a growing delay, and spread large sends over time
instead of firing them all at once.

## What does error 130429 mean?

Meta files it under throttling errors:

> "Cloud API message throughput has been reached."
> — [Meta, Cloud API error codes](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes#throttling-errors)

In other words, the number has used up its allowed message rate. In the Graph response, the
`message` field combines the code and its title: `(#130429) Rate limit hit`. Meta warns that these
titles will eventually be deprecated, so build your handling on the code, never on the text.

Throughput is counted **per number** and includes inbound and outbound messages of every type
([Meta, Throughput](https://developers.facebook.com/documentation/business-messaging/whatsapp/throughput)):

| Number                                                      | Maximum throughput     |
| ----------------------------------------------------------- | ---------------------- |
| Standard Cloud API number                                   | 80 messages/s          |
| Number automatically upgraded by Meta                       | up to 1,000 messages/s |
| Coexistence number (also used in the WhatsApp Business app) | 20 messages/s, fixed   |

Meta keeps returning 130429 until you are back under that ceiling.

## When does error 130429 happen?

* **A send loop with no pacing**: a script walking a 10,000-contact list as fast as the network
  allows crosses 80 messages/s within moments.
* **A coexistence number**: the 20 messages/s cap applies whatever your tier. A 100,000-recipient
  campaign takes at least 83 minutes on it.
* **Several senders on one number**: two workers, two campaigns, or a stream of API calls running
  next to a campaign all share the same throughput without coordinating.
* **A spike of inbound messages**: they count against the same throughput as your sends.

Do not confuse 130429 with two other ceilings:

| Ceiling         | What it counts                                                                             | Code                                                                                                                     |
| --------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| Throughput      | Messages per second, per number                                                            | `130429`                                                                                                                 |
| Pair rate limit | Messages to **the same** recipient                                                         | [`131056`](https://wa.genuka.com/en/docs/errors/131056)                                                                                       |
| Messaging limit | Unique recipients outside the service window per rolling 24 h, at business portfolio level | see [Meta, Messaging limits](https://developers.facebook.com/documentation/business-messaging/whatsapp/messaging-limits) |

### Where do you see it in Genuka WA?

| Channel                          | What you get                                                                    |
| -------------------------------- | ------------------------------------------------------------------------------- |
| `POST /api/v1/messages` response | `400`, `"error": "send_retryable"`, `meta.code: 130429`, `meta.retryable: true` |
| Campaign                         | Each recipient is retried; it only turns `failed` after three attempts          |
| Dashboard, Logs section          | The refused request, with the response body                                     |

```json title="400 — POST /api/v1/messages response"
{
  "error": "send_retryable",
  "message": "Cloud API message throughput has been reached.",
  "meta": {
    "errorClass": "retryable",
    "retryable": true,
    "code": 130429,
    "details": "Cloud API message throughput has been reached.",
    "traceId": "Az8or2yhqkZfEZ-_4Qn_Bam"
  }
}
```

The HTTP status is `400`, not `429`: decide whether to retry from `meta.retryable`, not from the
status.

## How do I fix error 130429?

1. **Retry with a growing delay.** A single send through `POST /api/v1/messages` is never retried by
   Genuka WA on your behalf: an interactive request should not take several seconds to fail quietly.
   Your code retries, with a wait that doubles on each attempt plus some jitter so your workers do
   not all restart in the same millisecond.
2. **Measure your real ceiling.** `GET /api/v1/numbers` returns, for each number, a
   `messagesPerSecond` field (20, 80 or 1,000) and `coexistence: true` when the number is also used
   in the app. Pace your sends on that value, not on a default of 80.
3. **Send in bulk through a campaign.** `POST /api/v1/campaigns`, then
   `/api/v1/campaigns/{id}/launch`: Genuka WA paces the run at the number's throughput and makes up
   to three attempts per recipient (waits of at most 500 ms, then 1 s, with jitter). A one-off
   130429 does not cost you the recipient. See the
   [campaigns API guide](https://wa.genuka.com/en/docs/guides/campaigns-api).

**Node.js**

```ts
const API = "https://wa.genuka.com/api/v1";

export async function sendWithBackoff(payload: unknown, maxAttempts = 5) {
  for (let attempt = 1; ; attempt++) {
    const response = await fetch(`${API}/messages`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.GENUKA_WA_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    // A 5xx can arrive as plain text (the proxy's "error code: 502"): JSON is not guaranteed.
    const json = await response.json().catch(() => null);
    if (response.ok) return json?.data; // { messageId, warning? }

    // meta.retryable is true for throughput limits (130429, 131056, 4, 80007), Meta internal
    // errors (131000, 5xx), stale media and 131048. That last one is retried later, not in this
    // loop (see the 131048 page). A 5xx with no `meta` does not tell whether the message left:
    // it is thrown as-is, check before resending to avoid a duplicate.
    const retryable = json?.meta?.retryable === true && json.meta.code !== 131048;
    if (!retryable || attempt >= maxAttempts) {
      throw new Error(`${response.status} ${json?.error ?? ""}: ${json?.message ?? ""}`);
    }
    const base = Math.min(30_000, 1_000 * 2 ** (attempt - 1));
    await new Promise((resolve) => setTimeout(resolve, base / 2 + Math.random() * (base / 2)));
  }
}
```

**Python**

```python
import os
import random
import time

import requests

API = "https://wa.genuka.com/api/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['GENUKA_WA_API_KEY']}",
    "User-Agent": "acme-crm/1.0 (+https://example.com)",
}


def send_with_backoff(payload, max_attempts=5):
    for attempt in range(1, max_attempts + 1):
        response = requests.post(f"{API}/messages", json=payload, headers=HEADERS, timeout=30)
        try:
            body = response.json()
        except ValueError:  # a proxy 5xx can arrive as plain text
            body = {}
        if response.ok:
            return body["data"]  # {"messageId": ..., "warning": ...}

        meta = body.get("meta", {})
        # 131048 is retryable, but later: see the 131048 page. A 5xx with no `meta` does not
        # tell whether the message left: it is raised as-is, check before resending.
        retryable = meta.get("retryable", False) and meta.get("code") != 131048
        if not retryable or attempt == max_attempts:
            raise RuntimeError(f"{response.status_code} {body.get('error')}: {body.get('message', '')}")
        base = min(30.0, 2 ** (attempt - 1))
        time.sleep(base / 2 + random.random() * base / 2)
```

**PHP**

```php
<?php
function send_with_backoff(array $payload, int $maxAttempts = 5): array
{
    for ($attempt = 1; ; $attempt++) {
        $ch = curl_init("https://wa.genuka.com/api/v1/messages");
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_USERAGENT => "acme-crm/1.0 (+https://example.com)",
            CURLOPT_HTTPHEADER => [
                "Authorization: Bearer " . getenv("GENUKA_WA_API_KEY"),
                "Content-Type: application/json",
            ],
            CURLOPT_POSTFIELDS => json_encode($payload),
        ]);
        $raw = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        curl_close($ch);
        // A proxy 5xx can arrive as plain text: JSON is not guaranteed.
        $json = is_string($raw) ? json_decode($raw, true) : null;
        $json = is_array($json) ? $json : [];

        if ($status >= 200 && $status < 400) {
            return $json["data"];
        }
        $meta = $json["meta"] ?? [];
        // 131048: later (see its page). A 5xx with no "meta" is thrown as-is: check before
        // resending, the message may have left.
        $retryable = ($meta["retryable"] ?? false) && ($meta["code"] ?? null) !== 131048;
        if (!$retryable || $attempt >= $maxAttempts) {
            throw new RuntimeException("{$status} " . ($json["error"] ?? ""));
        }
        $base = min(30.0, 2 ** ($attempt - 1));
        usleep((int) (($base / 2 + mt_rand() / mt_getrandmax() * $base / 2) * 1_000_000));
    }
}
```

**curl**

```bash
# Each number's throughput, to pace your sends on (stored values).
# To read them again from Meta: ?refresh=true&companyId=..., 25 numbers at most per call.
curl "https://wa.genuka.com/api/v1/numbers" \
  -H "Authorization: Bearer $GENUKA_WA_API_KEY"
# { "data": [ { "id": "con_1", "messagesPerSecond": 80, "coexistence": false, ... } ] }
```

## How do I prevent error 130429?

* **One sender per number.** Route every send for a number through a single queue, paced under
  `messagesPerSecond`. That is what stops two processes from stepping on each other.
* **Plan the duration of large sends.** At 20 messages/s, a coexistence number sends at most
  72,000 messages an hour. Announce the duration instead of discovering it.
* **Use media hosted by Meta** for fast sends: Meta recommends sending a media ID rather than a
  URL on your own servers to take advantage of higher throughput
  ([Meta, Throughput](https://developers.facebook.com/documentation/business-messaging/whatsapp/throughput)).
  With Genuka WA, `assetId` does exactly that.
* **Do not retry on a fixed interval.** A "three tries, one second apart" loop sends the same burst
  at the same moment and gets another 130429.

## Related error codes

* [131056](https://wa.genuka.com/en/docs/errors/131056): too many messages to **the same** recipient in a short time.
* [131048](https://wa.genuka.com/en/docs/errors/131048): sends restricted after blocks or spam reports, a quality
  problem rather than a speed problem.
* [4](https://wa.genuka.com/en/docs/errors/4) and [80007](https://wa.genuka.com/en/docs/errors/80007): the two other limits in the same
  family, which count **API calls** by the app and by the WhatsApp Business account, not messages
  sent.

## FAQ

### Can my number go up to 1,000 messages per second?

Yes, automatically and at no cost, when three conditions are met: an unlimited messaging limit, at
least 100,000 unique users messaged outside a service window within a rolling 24 hours, and a
quality rating of at least medium. During the upgrade, which takes up to a minute, the number
answers `131057` ([Meta, Throughput](https://developers.facebook.com/documentation/business-messaging/whatsapp/throughput)).
A coexistence number stays at 20 messages/s.

### Does Meta bill a message refused with 130429?

No. Meta only charges for a template message when it is delivered
([Meta, Pricing](https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing)),
and a message refused with 130429 never left. On the Genuka WA side, a refused send does not
consume your subscription quota either: only messages Meta accepted are counted.

### Why does Genuka WA answer 400 and not 429?

Because every Meta refusal on a send comes back as `400`, except token, permission and number
registration problems, which come back as `409`. Whether to retry is in `meta.retryable` and
`meta.errorClass`, which exist for that purpose.

### Can 130429 come from the Genuka WA API itself?

No. 130429 is a Meta code that Genuka WA relays as-is in `meta.code`: it is always about the
sending number's throughput at Meta, not about your API key.

## Sources

* [Meta — Error codes](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes)
* [Meta — Throughput](https://developers.facebook.com/documentation/business-messaging/whatsapp/throughput)
* [Meta — Messaging limits](https://developers.facebook.com/documentation/business-messaging/whatsapp/messaging-limits)
* [Meta — Pricing](https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing)
* [Meta — About the platform, rate limits](https://developers.facebook.com/documentation/business-messaging/whatsapp/about-the-platform#rate-limits)
