Errors, limits and best practices
Error format, codes, request limits and how to deal with Meta's rate limits.
Error format
{
"error": true,
"code": "validation_failed",
"user_message": "O nome da campanha é obrigatório",
"request_id": "0b6f…",
"errors": { "campaign_name": "O nome da campanha é obrigatório" }
}user_message is always in Brazilian Portuguese and can be shown to the end user (the example reads "The campaign name is required"). Branch your code on code and the HTTP status, never on the message text. Internal details (database, Meta) are never returned — they stay in the logs, linked to the request_id/log_id.
| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_request, invalid_json, selection_missing | Malformed body (the field is named in user_message and in issues) |
| 401 | unauthorized | No credential, revoked/expired key or invalid token |
| 403 | insufficient_scope | Missing permission (see required_scope) |
| 403 | selection_denied, forbidden | Archived client, account from another BM/disabled/inactive on Meta, another user's resource |
| 404 | not_found, campaign_not_found, post_not_found, client_not_found, client_archived | Resource does not exist (or the client is archived) |
| 409 | execution_running, conflict, ambiguous_client | Run already in progress / retry already done / more than one client matches the name (use the id) |
| 422 | validation_failed | Business rules — see errors per field |
| 422 | audiences_unavailable | Custom audience the ad account cannot use |
| 429 | rate_limited | Media Ops API limit (see Retry-After) |
| 429 | meta_rate_limited | Meta throttled the ad account (see retry_after_seconds) |
| 502 | upstream_error, meta_error | Meta or Google Drive failure |
API limits
Per API key (or OAuth app), per minute:
| Class | Limit | Examples |
|---|---|---|
| Read | 300 | GET, lookups |
| Write | 60 | Regular POST/PATCH/DELETE |
| Heavy | 10 | POST /campaigns, /campaigns/validate, /audiences/engagement, syncs |
Responses carry x-ratelimit-limit and x-ratelimit-remaining. On a 429, wait Retry-After seconds.
Meta limits
Meta rate-limits calls per ad account (code 17 and similar). During a creation run, the server waits and resends on its own — you will see progress.throttle.paused = true. An item is marked as an error only after 30 minutes of waiting.
For direct lookups (/meta/*), a block becomes 429 meta_rate_limited with retry_after_seconds.
Best practices
- Validate with
POST /campaigns/validatebefore creating. - Keep the
job_id. If your automation goes down, resume by polling/executions/{job_id}/progress— the run keeps going on the server. - Do not resend
POST /campaignsto "try again": usePOST /executions/{job_id}/retry, which reuses what was already created. - Use keys with the minimum permissions and an expiration date.
- Send your own
x-request-idif you want to correlate with your logs; it is echoed back in the response.
Updated about 23 hours ago

