# WhatsApp error 80007: account rate limit reached — fix

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

> WhatsApp error 80007: the WhatsApp Business Account hit its API call limit (200 or 5,000 per hour). Which calls count, and how to avoid it.

WhatsApp error 80007 means an application made too many calls on one WhatsApp Business account
within the hour: 200 by default, 5,000 for an active account with a registered number. The calls
that count are account management calls, such as templates and phone numbers, not message sends.
Wait, then call less often.

## What does error 80007 mean?

Meta lists it among the throttling errors:

> "The WhatsApp Business Account has reached its rate limit."
> — [Meta, Cloud API error codes](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes#throttling-errors)

Suggested fix: see the WhatsApp Business account rate limits, then "try again later or reduce the
frequency or amount of API queries the app is making".

Those limits are described in the platform overview: an app's requests on an account are counted
over a rolling hour, at 200 per hour, per app, per account by default, and 5,000 per hour for an
active account with at least one registered phone number
([Meta, About the platform](https://developers.facebook.com/documentation/business-messaging/whatsapp/about-the-platform#rate-limits)).
They cover these endpoints:

| Request                 | Endpoint                              |
| ----------------------- | ------------------------------------- |
| `GET`                   | `/{WABA_ID}`                          |
| `GET`, `POST`, `DELETE` | `/{WABA_ID}/assigned_users`           |
| `GET`                   | `/{WABA_ID}/phone_numbers`            |
| `GET`, `POST`, `DELETE` | `/{WABA_ID}/message_templates`        |
| `GET`, `POST`, `DELETE` | `/{WABA_ID}/subscribed_apps`          |
| `GET`                   | `/{WABA_TO_NUMBER_CURRENT_STATUS_ID}` |

Graph's general rate limit page also ties code `80008` to the "WhatsApp Business Management API"
limit type. Handle both the same way
([Meta, Rate limits](https://developers.facebook.com/docs/graph-api/overview/rate-limiting)).

## When does error 80007 happen?

* **Polling templates.** Re-reading the template list every minute to see whether one was approved
  costs 60 calls an hour on that account, before anything else.
* **Bulk template creation.** A script that submits, deletes and recreates dozens of templates back
  to back.
* **An account that is not active yet.** Until a number is registered on it, the account stays at
  200 calls per hour.

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

The limit is counted per application **and** per WhatsApp account. With Genuka WA the application
is Genuka's: every call made on a customer's account counts together, whether it comes from your
code, the dashboard or the client portal. On the other hand, one customer's account never uses up
another customer's limit.

On the Genuka WA side, these calls go through:

| Genuka WA endpoint                                               | Meta call counted                                                     |
| ---------------------------------------------------------------- | --------------------------------------------------------------------- |
| `POST /api/v1/templates`                                         | `POST /{WABA_ID}/message_templates`                                   |
| `DELETE /api/v1/templates/{id}`                                  | `DELETE /{WABA_ID}/message_templates`                                 |
| `POST /api/v1/templates/sync`, `GET /api/v1/templates?sync=true` | `GET /{WABA_ID}/message_templates`, once per WhatsApp account covered |

Genuka puts code 80007 in the `retryable` class. A refused template creation comes back as
`422 meta_rejected` with `meta.retryable: true`, or as a `502` if Meta answered with a 429 or a 5xx.
A refresh stays a `200` as long as at least one account answered: the limited account shows
`"ok": false` with Meta's reason in `error`. If every account refused, the response is a
`502 meta_rejected`.

A `502` reaches you without a JSON body: our CDN replaces it with the single line
`error code: 502` ([API reference](https://wa.genuka.com/en/docs/api)). Read the HTTP status before the body, and treat
this case as a transient failure.

## How do I fix error 80007?

1. **Stop asking for what arrives on its own.** A template review arrives by webhook, as a
   `message_template_status_update` event ([Webhooks](https://wa.genuka.com/en/docs/webhooks)). Subscribe an endpoint to it
   and remove the read loop.
2. **Keep the refresh for repairs.** `POST /api/v1/templates/sync` exists to catch up on a missed
   webhook: once an hour is plenty, and `companyId` or `connectionId` narrows it to the one account
   involved ([API reference](https://wa.genuka.com/en/docs/api#templates)).
3. **Retry what was refused, later.** A template creation marked `retryable` can be submitted again
   after a delay; the limit frees up as the rolling hour moves on.
4. **Spread bulk creation out.** Submit a new customer's templates in small batches rather than in one
   burst.

If you call Graph yourself, the `X-Business-Use-Case-Usage` header gives the share of calls used
(`call_count`) and, once throttled, `estimated_time_to_regain_access`, the number of minutes before
things return to normal
([Meta, Rate limits](https://developers.facebook.com/docs/graph-api/overview/rate-limiting)).

**Node.js**

```ts
// One refresh an hour, narrowed to one customer, reporting the accounts that failed.
const response = await fetch("https://wa.genuka.com/api/v1/templates/sync", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.GENUKA_WA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ companyId: "cmp_1" }),
});
// A 502 arrives without JSON: our CDN replaces it with "error code: 502".
const json = await response.json().catch(() => null);

if (!response.ok) {
  console.error("No account answered", response.status, json?.message ?? "");
} else {
  for (const waba of json.data.wabas.filter((w: { ok: boolean }) => !w.ok)) {
    console.warn("Account not refreshed, retry next hour", waba.wabaId, waba.error);
  }
}
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://wa.genuka.com/api/v1/templates/sync",
    headers={"Authorization": f"Bearer {os.environ['GENUKA_WA_API_KEY']}"},
    json={"companyId": "cmp_1"},
    timeout=60,
)
try:
    body = response.json()
except ValueError:
    body = {}  # a 502 arrives without JSON: our CDN replaces it with "error code: 502"

if not response.ok:
    print("No account answered", response.status_code, body.get("message"))
else:
    for waba in body["data"]["wabas"]:
        if not waba["ok"]:
            print("Account not refreshed, retry next hour", waba["wabaId"], waba.get("error"))
```

**PHP**

```php
<?php
$ch = curl_init("https://wa.genuka.com/api/v1/templates/sync");
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(["companyId" => "cmp_1"]),
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); // 0 when the request never completed
curl_close($ch);
// A 502 arrives without JSON: our CDN replaces it with "error code: 502".
$body = is_string($raw) ? json_decode($raw, true) : null;

if ($status === 0 || $status >= 400 || !is_array($body)) {
    error_log("No account answered: HTTP $status " . ($body["message"] ?? ""));
} else {
    foreach ($body["data"]["wabas"] as $waba) {
        if (!$waba["ok"]) error_log("Account not refreshed: {$waba['wabaId']} " . ($waba["error"] ?? ""));
    }
}
```

**curl**

```bash
# A 502 arrives as plain text ("error code: 502"), which jq then fails to read.
curl -s -X POST https://wa.genuka.com/api/v1/templates/sync \
  -H "Authorization: Bearer $GENUKA_WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "companyId": "cmp_1" }' \
  | jq '.data.wabas[] | select(.ok == false)'
```

## How do I prevent error 80007?

* **Webhooks first.** A template status, a quality change or an account update reaches you without
  you having to ask.
* **A scheduled refresh, not one triggered by page views.** A dashboard that re-reads Meta every
  time it opens multiplies calls by the number of visitors.
* **Connect the number before creating templates.** An active account with a registered number gets
  5,000 calls per hour instead of 200.

## Related error codes

* [4](https://wa.genuka.com/en/docs/errors/4): the call limit of the whole application.
* [130429](https://wa.genuka.com/en/docs/errors/130429): a number's message throughput.
* `80008`: the code Graph's rate limit page ties to the WhatsApp Business Management API.

## FAQ

### Why is my account limited to 200 calls per hour instead of 5,000?

Meta reserves 5,000 calls per hour for active accounts with at least one registered number. An
account created without a number, or whose number is not registered yet, stays at 200.

### Do message sends count against this limit?

The endpoints Meta lists for this limit are account management ones, not sends. Sends have their
own caps, including per-number throughput, code [130429](https://wa.genuka.com/en/docs/errors/130429).

### Can one customer's account exhaust another's limit?

No. The limit is counted per application and per WhatsApp account: each account has its own.

## Sources

* [Meta — Error codes](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes)
* [Meta — About the platform, rate limits](https://developers.facebook.com/documentation/business-messaging/whatsapp/about-the-platform#rate-limits)
* [Meta — Graph API, Rate limits](https://developers.facebook.com/docs/graph-api/overview/rate-limiting)
