Errors, limits and retries

Keep retries safe and distinguish an accepted send from confirmed delivery.

Error responses

{"error":{"code":"rate_limited","message":"Too many requests","retry_after":30}}
CodeHTTPNext step
invalid_api_key401Key is invalid, revoked, or account access is unavailable.
insufficient_permission403Grant the required scope or check your inbox storage consent.
plan_required403API keys and webhooks need the Max plan. upgrade_url links to pricing.
unknown_tool404Use a name from GET /api/v1/tools.
invalid_input400 / 415 / 413Validate JSON against the tool schema and size limits.
rate_limited429Wait for Retry-After; retain the same idempotency key.
daily_limit_reached429This personal WhatsApp number used its daily API sends. Retry-After counts to 00:00 UTC.
whatsapp_not_connected409Check /api/v1/me and reconnect the intended channel.
tool_error422 / 503Inspect the error and quote X-Request-Id to support.
outcome_unknown409Inspect get_operation or get_send_status. Do not send again with a new key.

Limits

Read tools allow 600 requests per minute per key and write tools 120. Each account may have ten active keys and ten webhook endpoints.

Personal WhatsApp numbers can be banned by WhatsApp for sending too much, so sends through the API are capped at 50 messages a day per number and 5 a minute. AI agents such as Claude and ChatGPT have their own budget of 15 a day on the same number. Messages, files, reactions, polls and group messages all count. Counts reset at 00:00 UTC.

The official WhatsApp Business API has no WhatSync daily limit when you use an API key. Meta's own messaging limits and template rules still apply.

One intended action, one stable key

Idempotency keys are 8–128 characters: letters, numbers, period, underscore, colon or hyphen. A body idempotencyKey takes precedence over the header. Reuse exactly the same inputs and key for retries. When replying to a webhook, use its event ID. A different action requires a different key. Management operations may return an operation_id and a result envelope; use get_operation to inspect an uncertain outcome. Never treat a timeout as proof that nothing was sent.