Groupilot Docs

Limits and errors

Rate limits, daily quotas, response headers, the error format and the list of error codes.

No credits and no per-call billing: the API is included in the subscription. Usage is bounded by limits instead.

Rate limits

WhatLimit
Reads (GET)120 requests per minute per key
Writes (POST, PUT, PATCH, DELETE)30 requests per minute per key
PUT /v1/sequences/sync10 calls per minute per project
Whole API1,000 requests per minute across all keys, as a circuit breaker

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. GET /me returns the limits that apply to your key under limits. A 429 carries Retry-After in seconds: wait that long, then retry.

Daily quotas (scope send)

WhatQuota
One-off messages30 per day per project
Broadcasts8 per day per project, 200 communities each
Media uploads50 per day per project

Quotas count API calls only, not what you do in the app. They protect the reputation of the WhatsApp number. Broadcasts take 1 to 200 communities each.

Other limits

WhatLimit
API keys5 active per project
Webhook endpoints5 per project
Messages per sequence200
Sequences per account200
Text of a message10,000 characters
Media URL2,000 characters
Pollquestion 255 characters, 2 to 12 options of 100 characters
Mediaimage 16 MB (jpg, jpeg, png), video 40 MB (mp4, mov, 3gp), voice 16 MB (ogg, m4a, wav, mp3), document 100 MB; import from URL 40 MB
Backup admin selection250 communities per confirmation; apply and retry take one community unless bulk apply is enabled (otherwise 409 bulk_apply_disabled)
Paginationsee below

Error format

{ "error": { "code": "validation_error", "message": "timing must match J-1 10:00 or H-1", "field": "messages[1].timing" } }

code is stable and meant for your code. message is for humans and can change. field is present when the error concerns one field.

Error codes

HTTPCodeWhen
400invalid_jsonRequest body is not valid JSON
401unauthorizedMissing token
401invalid_api_keyInvalid, revoked or expired API key or OAuth token
403api_not_enabledAPI not enabled on the project
403insufficient_scopeToken lacks the scope
403forbiddenBroadcast refused because the account cannot send (billing or automation blocked), see message
404not_foundUnknown id, or id of another project
409conflictAction refused in the current state (stopped event, send no longer pending, broadcast already started, sequence still linked to an event, number not connected…), see message
409idempotency_in_progressA request with this Idempotency-Key is still being processed, or ended without a known outcome. See Retry-After, and check the result before sending again with a new key
409bulk_apply_disabledBackup admin apply or retry on several communities while bulk apply is off
409community_update_not_editableA community update step cannot be edited for one occurrence
400validation_errorBody failed validation, see field
400nonexistent_local_time / ambiguous_local_timeLocal time falls in a DST gap or overlap
400malformed_scheduled_atBroadcast date malformed
400scheduled_at_not_futureBroadcast time not in the future
400invalid_time / invalid_calendarOccurrence: scheduled_at_local is not a real date or time
413payload_too_largeRequest body over 4 MiB
422idempotency_key_reusedSame Idempotency-Key with a different body
429rate_limitedRate limit or daily quota reached, see Retry-After
500 / 503internal_errorError on our side. A 5xx raised once the request is being processed keeps the Idempotency-Key on purpose, for up to 24 hours: a retry with the same key answers 409 idempotency_in_progress with Retry-After, never a second send. After a 5xx on a write (except rate_limit_unavailable, which can be retried), check the result first, then send again with a new key only if nothing happened
503rate_limit_unavailableRate limiter unavailable, retry after Retry-After
503channel_unavailableThe communities of the number could not be listed, nothing was created. After Retry-After, send again, with a new Idempotency-Key if you set one
4xx / 503invalid_e164, not_whatsapp_user, contact_not_saved, same_as_primary, stale_preview, preview_expired, provider_unavailable…Backup admin: number or preview refused, see message

Idempotency

Send an Idempotency-Key header (1 to 128 characters: letters, digits, -, _, . or :; a UUID is fine) on POST, PUT and PATCH. For 24 hours, the same key with the same body returns the stored response; the same key with another body returns 422 idempotency_key_reused.

Pagination

Every list returns its elements under items. Three styles, chosen per endpoint and visible in the reference:

EndpointsStyle
GET /events/{id}/jobs, GET /jobs/{id}/deliveriesCursor: limit (100 by default, 500 max) and cursor; the response carries next_cursor, null on the last page. A cursor is only valid with the same order.
GET /events/{id}/occurrences, GET /broadcasts/{id}/recipientsPages: page (from 1); the response carries page, page_size, total (and counts per filter for occurrences). Recipients come 50 per page.
GET /notificationslimit (20 by default, 100 max), offset, unread_only.

GET /events, GET /communities, GET /sequences and GET /broadcasts return the whole list.

Your current limits are also returned by GET /me under limits.

Versioning

Everything lives under /v1. Fields can be added at any time; your parser must ignore unknown fields. Removing or renaming anything means a /v2, announced in the changelog.

On this page