WhatsApp API

Errors & Limits

StartMessaging WhatsApp API error codes by HTTP status, how Meta refusals map to them, when to retry, per-plan rate limits, RateLimit headers and size limits.

Error format

Every error has the same shape:

{
  "statusCode": 422,
  "error": {
    "code": "TEMPLATE_NOT_APPROVED",
    "message": "The template is not approved.",
    "details": { "status": "PENDING" }
  },
  "meta": { "requestId": "...", "timestamp": "2026-10-02T10:00:00.000Z", "path": "/api/v1/messages" }
}
  • Branch on error.code, never on message. Messages are for humans and may change.
  • details adds structured context for some codes (listed below).
  • Quote meta.requestId when you contact support. Your recent requests are also in Developers → Logs (the last 500 requests from the past 7 days).

An error’s meta also has path, the request path without its query string. Successful responses use { "data": ..., "meta": ... }, where meta has only requestId and timestamp.

Error codes

Each route’s own list of codes is in the API Reference.

400 Bad Request: fix the request

CodeMeaning
VALIDATION_ERRORA field is missing, has the wrong type, or is not accepted on this route.
INVALID_CURSORThe cursor did not come from the API, or came from a different query (for example a list without updatedSince). Keep every parameter unchanged while paging.
INVALID_IDEMPOTENCY_KEYThe Idempotency-Key is not 1–255 printable ASCII characters without spaces.
MOBILE_INVALIDThe phone number is not a valid number.
MOBILE_COUNTRY_UNRESOLVEDThe number is valid but belongs to no country (a satellite or other non-geographic number).
WABA_NOT_CONNECTEDNo WhatsApp number is connected, the connection’s token expired or lost a permission, or the sending number is no longer registered. Reconnect in the dashboard.
BAD_REQUESTThe body is not valid JSON or could not be read (for example it ended early), or the path has a broken % escape.

401 Unauthorized

CodeMeaning
INVALID_API_KEYThe key is missing, malformed or revoked. The answer is the same in every case.

402 Payment Required

CodeMeaning
SUBSCRIPTION_REQUIREDThe workspace is read-only. Razorpay gave up retrying a failed renewal, so the workspace keeps its old plan but every write, sends included, is refused until billing is fixed in the dashboard. Reads still work. The same code is also answered for the few minutes between a trial or cancelled subscription ending and the workspace moving to Developer Free.
PLAN_MESSAGE_LIMIT_REACHEDThis month’s API message allowance is used up. details has limit, stopAt, sent, period and resetsAt. See Monthly API message allowance.
META_PAYMENT_REQUIRED / META_BILLING_ERRORWhatsApp could not bill the payment method on your own WhatsApp Business Account. Fix it in WhatsApp Manager. This is never a StartMessaging charge.

A workspace whose trial ends, whose subscription is cancelled or ends, or whose checkout was never finished is not locked: it moves to Developer Free, and the API keeps working within that plan’s limits. A failed renewal first goes to past due and stays writable while Razorpay retries.

403 Forbidden

CodeMeaning
PLAN_FEATURE_REQUIREDYour plan lacks the developer API, or a route’s extra feature (for example the team inbox for PATCH /conversations/{id}, interactive messages, or the sales pipeline for POST /conversions).
PLAN_LIMIT_REACHEDA plan cap is full. details has limit and max. max_contacts: a send to a number that is not a contact yet, or POST /contacts for a new one. max_api_webhooks: POST /webhooks. Delete what you no longer need or upgrade; retrying will not help.
INSUFFICIENT_SCOPEThe key lacks the route’s scope. details has required and granted.
MESSAGE_WINDOW_CLOSEDA free-form message outside the 24-hour window. Send a template.
WABA_SUSPENDED / WABA_RESTRICTEDMeta suspended the WhatsApp Business Account, or restricted it for a policy violation.
RECIPIENT_COUNTRY_RESTRICTEDMeta does not let this account send this kind of message to the recipient’s country.

404 Not Found

CodeMeaning
CONTACT_NOT_FOUND, CONVERSATION_NOT_FOUND, MESSAGE_NOT_FOUND, TEMPLATE_NOT_FOUND, CAMPAIGN_NOT_FOUND, WEBHOOK_NOT_FOUND, PIPELINE_STAGE_NOT_FOUND, WORKSPACE_NOT_FOUNDIt does not exist, or belongs to another workspace. The API never says which.
NOT_FOUNDNo such route. Check the method and the path.

409, 413, 415

CodeHTTPMeaning
IDEMPOTENCY_KEY_IN_USE409A request with this key is still running. Retry shortly.
PAYLOAD_TOO_LARGE413The body is over the limit: 100 KB for JSON. details.maxBytes gives the limit.
UNSUPPORTED_MEDIA_TYPE415The body uses a character set or content encoding the API cannot read. Send UTF-8 JSON.

422 Unprocessable: the request cannot be done as asked

CodeMeaning
CONTACT_OPTED_OUTThe contact replied STOP, turned off your marketing messages in WhatsApp, or you recorded optedIn: false. They must opt back in first. Deleting the contact does not change this: the number stays opted out.
MESSAGE_UNDELIVERABLEWhatsApp would not deliver to this number (not on WhatsApp, or Meta’s per-customer marketing limit).
MESSAGE_REJECTEDWhatsApp refused the request itself, for example the wrong number of template variables. The same request fails the same way. details.metaCode is Meta’s error number.
TEMPLATE_NOT_APPROVEDThe template is not approved. details.status gives its status.
TEMPLATE_HEADER_MEDIA_REQUIREDThe template has an image, video or document header. Send the file’s URL as template.headerMediaUrl.
TEMPLATE_BUTTON_PARAM_REQUIREDA template button needs its value on every send (a link with a variable, a coupon code). Send it in template.buttonParameters. On an authentication template it means template.parameters was empty too: put the code in parameters. WhatsApp Form (flow) buttons never cause it. details lists each missing button’s index and buttonType.
TEMPLATE_CAROUSEL_CARDS_INCOMPLETEA carousel was sent without everything each card needs, or with a different number of cards than it was created with. details.expected is the card count, details.received what you sent, and details.problems lists each gap as card (from 1) and missing (media, variables or button, with buttonIndex).
TEMPLATE_OFFER_EXPIRY_REQUIREDA limited-time offer that shows its end time was sent without template.offerExpiresAt.
TEMPLATE_OFFER_EXPIRY_INVALIDtemplate.offerExpiresAt has already passed. Send a future time.
TEMPLATE_VARIABLES_NOT_REGISTEREDMeta registered this template without its variables, so it can’t be filled in. Duplicate it in the dashboard and send the copy.
IDEMPOTENCY_KEY_REUSEDThe Idempotency-Key was used with a different body or route, or by another of your API keys.
ASSIGNEE_NOT_MEMBERThe assignee is not an active member who may be assigned.
WEBHOOK_URL_NOT_ALLOWEDThe webhook URL is not allowed. details.reason says why. See Webhooks.

429 Too Many Requests

CodeMeaning
RATE_LIMIT_EXCEEDEDYou hit your plan’s API limit. details has limit, windowSeconds and scope (requests or sends). Retry-After is exact.
PHONE_DAILY_LIMIT_REACHEDA WhatsApp-side limit on the sending number. Try later. Retry-After (60 seconds) is the least time to wait, since WhatsApp does not say when its limits lift.

502 Bad Gateway

CodeMeaning
WABA_CONNECT_FAILEDA transient WhatsApp failure (a 5xx, throttling, maintenance or no answer). Whether the message went is unknown. Retry later with the same Idempotency-Key.

BAD_REQUEST, NOT_FOUND, PAYLOAD_TOO_LARGE and UNSUPPORTED_MEDIA_TYPE are answered before the API key is read, so a request that also has a bad key gets them rather than 401.

When WhatsApp refuses a send

When Meta refuses a send, you get one of the codes above, and on MESSAGE_REJECTED Meta’s number as details.metaCode, so you can look it up in Meta’s error code reference. Only a payment-method failure is ever reported as a payment problem.

Meta codeMeaningAPI answer
131042Payment method problem on your WhatsApp Business Account402 META_PAYMENT_REQUIRED
131047More than 24 hours since the customer last replied403 MESSAGE_WINDOW_CLOSED (send a template)
368Account restricted for a policy violation403 WABA_RESTRICTED
131031Account locked403 WABA_SUSPENDED
130497Not allowed to message users in that country403 RECIPIENT_COUNTRY_RESTRICTED
131026, 131049Undeliverable, or Meta’s per-customer marketing limit422 MESSAGE_UNDELIVERABLE
131050The customer stopped your marketing messages422 CONTACT_OPTED_OUT
130429, 131048, 131056, 131064Throughput, spam-quality, same-recipient and template-category limits429 PHONE_DAILY_LIMIT_REACHED
190, 0, 3, 10, 131005, 200–299The connection’s access token expired, was revoked or lost a permission400 WABA_NOT_CONNECTED (reconnect in the dashboard)
33, 133010The sending number was deleted, or is no longer registered on the Cloud API400 WABA_NOT_CONNECTED (reconnect in the dashboard)
100, 131008, 131009, 131051, 131053, 132000, 132001, 132005, 132012, 132015, 132016The request itself: an invalid or missing value, an unsupported message type, media Meta could not fetch, the wrong number of template variables, a template missing in that language, too long once filled in, or paused or disabled for quality422 MESSAGE_REJECTED (details.metaCode)
131057The account is in maintenance502 WABA_CONNECT_FAILED (retry later)

Any other refusal Meta marks final is also 422 MESSAGE_REJECTED with details.metaCode. Anything transient (a 5xx, throttling, a timeout, no answer) is 502 WABA_CONNECT_FAILED.

A failure Meta reports later, on a delivery receipt, arrives as a message.failed webhook with Meta’s own failureCode instead.

When to retry

ResponseWhat to do
429Wait for Retry-After seconds, then retry with the same Idempotency-Key.
5xx (including 502 WABA_CONNECT_FAILED)Retry later, with backoff, with the same Idempotency-Key.
409 IDEMPOTENCY_KEY_IN_USERetry shortly with the same key.
Any other 4xxDo not retry unchanged. It fails the same way until the request, the plan or the account changes.

For a code you do not recognise, decide by its HTTP status using this table. A 400 releases the Idempotency-Key, so after you fix the problem (or reconnect WhatsApp), a retry with the same key really sends. A 422 is replayed. See Pagination & Idempotency.

Rate limits

Limits are per workspace, by plan. More API keys never buy more throughput.

PlanRequests / minuteMessages sent / hour
Free trial300100
Developer Free602,000
Developer 1K12010,000
Developer 10K30010,000
Developer 100K1,20010,000
Team30010,000
Business1,20010,000
Enterprise1,20010,000
  • Every counted request spends the per-minute request budget.
  • Sends (POST /api/v1/messages, POST /api/v1/trigger/send) also spend the hourly send budget. A send refused with a 4xx (by us or by Meta) gives it back; a 5xx keeps the charge.
  • Windows are fixed and aligned to the clock in UTC. The hourly window reopens at the top of each UTC hour (half past the hour in India).
  • GET /api/v1/me shows your plan’s numbers and live usage of both budgets.

Rate-limit headers

Every response to a counted request, success or error, carries:

HeaderMeaning
RateLimit-PolicyEvery window the request counted against, such as 60;w=60 (requests per minute) and, on a send, 100;w=3600 (sends per hour), with your plan’s numbers.
RateLimit-LimitThe limit of the window that will refuse you first.
RateLimit-RemainingWhat is left in that window.
RateLimit-ResetWhole seconds until that window reopens (never 0).
Retry-AfterOn every 429: seconds to wait.
HTTP/1.1 429 Too Many Requests
RateLimit-Policy: 60;w=60, 2000;w=3600
RateLimit-Limit: 2000
RateLimit-Remaining: 0
RateLimit-Reset: 1260
Retry-After: 1260

Requests refused before they are counted carry none of these headers: 401 INVALID_API_KEY, 402 SUBSCRIPTION_REQUIRED, 403 INSUFFICIENT_SCOPE, 403 PLAN_FEATURE_REQUIRED for a plan without the API, the team inbox (PATCH /conversations/{id}) or the sales pipeline (POST /conversions), 404 WORKSPACE_NOT_FOUND, and the answers given before the key is read (400 BAD_REQUEST, 404 NOT_FOUND, 413, 415). The 403 PLAN_FEATURE_REQUIRED for interactive messages is checked after the rate limit, so it is counted and carries the headers. Read the headers when they are present and never treat a missing one as zero.

Monthly API message allowance

Each plan also includes a number of messages a month sent through the API. Only sends WhatsApp accepted count; a refused send gives its slot back. The month runs on India time and starts again on the 1st.

PlanAPI messages / monthWhen sends stop
Free trial1,000At 1,000, authentication templates included
Developer Free100At 100, authentication templates included
Developer 1K1,000At 3,000 (3× the allowance); authentication templates never stop
Developer 10K10,000At 30,000; authentication templates never stop
Developer 100K100,000At 300,000; authentication templates never stop
Team, BusinessUnlimited (fair use)Never

Past the stop point, sends answer 402 PLAN_MESSAGE_LIMIT_REACHED with details.sent, details.stopAt and details.resetsAt. Nothing upgrades or charges by itself. GET /api/v1/me returns the same numbers in messages, so you can warn your own users before sends stop.

Meta’s messaging limit

Meta’s own messaging limit applies on top of ours: the number of unique customers your business can message in a rolling 24 hours. It is set per business portfolio and shared by all its numbers. Your current limit and quality rating are in the dashboard and in GET /api/v1/phone-numbers. Hitting a WhatsApp-side limit returns 429 PHONE_DAILY_LIMIT_REACHED.

Message and template limits

LimitValue
Text message body4,096 characters
Template body1,024 characters
Template header and footer60 characters each
Template buttonsUp to 10 (at most 2 URL buttons and 1 phone button)
Coupon code (couponCode)15 characters (WhatsApp’s limit)
Carousel2–10 cards; card text up to 160 characters; up to 2 buttons per card
Interactive reply buttonsUp to 3 (titles up to 20 characters)
Interactive list rowsUp to 10 (row and section titles up to 24 characters; the list’s buttonLabel up to 20)
Media URLsMust be https:// (Meta fetches the file)
JSON request body100 KB
Idempotency-Key1–255 printable ASCII characters, no spaces; honoured for 24 hours
clientReference1–256 printable ASCII characters (spaces allowed)

Free-form messages (text, media, interactive) reach a customer only within 24 hours of their last message. Outside the window, send an approved template.

Webhook limits

LimitValue
EndpointsFree trial 5, Developer 2, Team 1, Business 5, Enterprise unlimited
URLHTTPS, port 443, public address, up to 2,048 characters
Response timeAnswer 2xx fast. 5 seconds each to connect, to send the response headers and between parts of the body; 10 seconds in all per request
Retries1 min, 5 min, 30 min, 2 h, 6 h, 12 h
Auto-disableAfter 50 exhausted deliveries in a row (each used all 7 attempts), or after 24 hours of failing with at least 3 exhausted

FAQ

Why do I get 401 for every request? The key is missing, malformed or revoked. Send Authorization: Bearer sm_live_... with a key from Developers → API keys. The API does not say which of these it was.

Is it safe to retry a send that timed out? Yes, if you sent an Idempotency-Key. Retry with the same key: if the first request went through, you get its original response back and nothing is sent twice.

Do more API keys raise my rate limit? No. Limits are per workspace. Upgrade your plan for higher limits.

I got 402 META_PAYMENT_REQUIRED. Do I owe StartMessaging money? No. Meta could not charge the payment method on your own WhatsApp Business Account. Fix it in WhatsApp Manager.

I got 402 PLAN_MESSAGE_LIMIT_REACHED. What now? This month’s API messages are used up. details.resetsAt says when the count starts again. To send more before then, choose a bigger Developer size or the Team or Business plan. On a paid Developer plan, authentication templates (OTPs) keep sending.

My subscription ended. Does the API stop? No. A workspace whose trial or subscription ends unpaid moves to Developer Free, and the API keeps working within its limits. Only a renewal Razorpay gives up retrying makes the workspace read-only (402 SUBSCRIPTION_REQUIRED on writes) until billing is fixed. Writes can also get that 402 for a few minutes after a trial or cancelled period ends, until the move to Developer Free.

What does details.metaCode mean on MESSAGE_REJECTED? It is Meta’s own error number. Look it up in Meta’s error code reference to see exactly what WhatsApp objected to.