Guides
Errors, limits and idempotency
The error format, how to retry safely, and what rate limits, plan limits and pagination look like.
Errors
Every error has the same body:
{
"error": {
"code": "account_needs_reauth",
"message": "The Facebook Page connection has expired. Reconnect the account.",
"request_id": "6b1f0c2e-3d4a-4b5c-8d9e-0f1a2b3c4d5e",
"details": { "account_id": "0192a7c1-6a10-7b22-8e31-4c5d6e7f8a01" }
}
}- Branch on
code, never onmessage. Codes are stable; messages can change. request_idis also in theX-Request-Idheader of every response, successful or not. Quote it when you write tohello@wasparks.com.detailsdepends on the code. It isnullwhen there is nothing to add.
What details holds:
| Code | details |
|---|---|
validation_failed |
fields: a list of { field, code, message, account_id }. field is the path in your request, such as account_ids[1]. account_id is set when the problem is for one account. |
media_not_ready |
media_id |
quota_exceeded |
limit, used, max |
platform_rejected |
platform, platform_code, platform_subcode, platform_message. All four are always there, and null when unknown. |
Every code, with its status and what to do, is in Error codes.
What to retry
| Status | Retry? |
|---|---|
400, 401, 402, 403, 404 |
No. Fix the request, the key or the plan first. |
409 idempotency_conflict |
No. You reused an idempotency key with a different request. |
409 invalid_state |
No. The resource is not in a state that allows this. Read it and decide. |
422 account_needs_reauth |
No. Reconnect the account. |
422 reply_window_closed, 422 platform_rejected |
No. The platform said no. |
429 |
Yes, after the Retry-After header says. |
500, 503 |
Yes, with backoff. Use an idempotency key where the call supports one. |
| Network error or timeout | Yes, with the same idempotency key. |
Idempotency
On POST /v1/posts, POST /v1/comments/{commentId}/replies and POST /v1/conversations/{conversationId}/messages, send an Idempotency-Key header: any unique string, such as a UUID you generate per action.
- Same key, same org, within 24 hours: you get the original response back and nothing is repeated.
- Same key with a different request:
409 idempotency_conflict. - Keys are scoped to the org. With a partner key that is the org named in
X-Tenant-Id.
Generate the key before the first attempt and reuse it on every retry of that action. A new key for each retry defeats the purpose.
Rate limits
Calls are limited per key and per org. Responses carry:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Calls allowed in the current window. |
X-RateLimit-Remaining |
Calls left. |
X-RateLimit-Reset |
When the window resets. |
Over the limit you get 429 rate_limited and a Retry-After header. Wait that long, then continue. With a partner key in operator mode, a call counts against both the partner key and the org it acts on.
429 platform_rate_limited is different: the social platform is limiting that account, not us. Back off for that account and try later. See Platform notes for limits that are fixed by the platforms.
Plan limits
Each org is on a plan. A plan can limit social_accounts, posts_per_month, members and workspaces; a limit that is left out is unlimited. A call that would go over returns 402 quota_exceeded, with limit, used and max in details.
GET /v1/org shows the org's plan limits and current use. GET /v1/usage shows counts over time. See Billing and usage.
Pagination
Lists that can grow, such as posts, comments and conversations, take limit (default 25, maximum 100) and cursor, and answer:
{ "data": [], "next_cursor": "eyJvIjoyNX0" }Pass next_cursor as cursor to get the next page, with the same limit and filters. A null next_cursor is the last page. Short lists, such as accounts, workspaces, plans, redirect URIs, API keys and webhook subscriptions, return everything at once with next_cursor: null.
Next: Platform notes