Errors and limits
Error format
Section titled “Error format”Every error answer has the same JSON shape:
{"error":{"code":"validation","message":"Please check the highlighted fields.","fields":{"email":"Enter a valid email address."}}}| Key | Meaning |
|---|---|
code |
A stable, machine-readable code. Branch on this. |
message |
A human-readable text in the language of Accept-Language (en or hu). It can change, so don’t parse it. |
fields |
Only for errors about the request’s content: the problem per key, as human-readable text. Nested keys use dots (fields.rendelesek_szama, content.html, requests.0). |
The same request with Accept-Language: hu:
{"error":{"code":"validation","message":"Ellenőrizd a megjelölt mezőket.","fields":{"email":"Adj meg érvényes e-mail-címet."}}}A method the path doesn’t take answers 405 with the Allow header:
curl -i -X PATCH "$ECHO_API/groups" -H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" -d '{"name":"x"}'HTTP/1.1 405 Method Not AllowedAllow: OPTIONS, GET, POSTContent-Type: application/json
{"error":{"code":"method_not_allowed","message":"This method is not allowed here."}}A body that isn’t valid JSON, or isn’t one JSON object, answers 400:
curl -X POST "$ECHO_API/groups" -H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" -d '{"name":'{"error":{"code":"invalid_json","message":"The request body is not a valid JSON object."}}A value of the wrong type is 422 validation with the key in fields, for example {"name":5} gives "fields":{"name":"Invalid value."}.
HTTP status codes
Section titled “HTTP status codes”| Status | Meaning |
|---|---|
200, 201, 202, 204 |
Done; created; accepted for background work; done without a body. |
400 |
The body isn’t valid JSON or isn’t one JSON object (invalid_json). |
401 |
No valid token (unauthenticated). |
403 |
The token can’t do this (forbidden), or the address, the workspace or two-factor rules stop it. |
404 |
Unknown ID or path (not_found). |
405 |
The path exists but not with this method (method_not_allowed). The Allow header lists the methods it takes. |
409 |
The request conflicts with the current state (see the codes below). |
413 |
The body is too large (too_large). |
415 |
The body isn’t application/json (unsupported_media_type). |
422 |
The content was understood but refused (validation and others below). |
429 |
Rate limit reached (rate_limited). |
503 |
A list or count took too long (list_timeout). Narrow the filter and try again. |
Error codes
Section titled “Error codes”| Code | Status | When |
|---|---|---|
unauthenticated |
401 | The token is missing, unknown or revoked, or its creator left the workspace or was disabled. |
ip_not_allowed |
403 | The token is limited to other IP addresses. |
workspace_suspended |
403 | The workspace is suspended. |
twofa_enforced |
403 | The workspace requires two-factor authentication and the token’s creator hasn’t set it up. The token works again once they do. |
forbidden |
403 | The token’s access level or its creator’s role doesn’t allow this. |
not_found |
404 | The ID doesn’t exist in this workspace, or the path is unknown. |
method_not_allowed |
405 | The path doesn’t take this method; see the Allow header. |
invalid_json |
400 | The body isn’t valid JSON or isn’t one JSON object. |
validation |
422 | The request’s content is wrong; see fields. |
unsupported_media_type |
415 | Send Content-Type: application/json. |
too_large |
413 | Over 1 MB (10 MB for an import). |
rate_limited |
429 | See Rate limits. |
name_taken |
409 | A group or field with this name exists. |
in_use |
409 | A segment, campaign or workflow uses the group or segment you want to delete; the message names them. |
builtin_field |
409 | Built-in fields can’t be renamed or deleted. |
webhook_limit |
409 | The workspace has 20 webhooks. |
unknown_tags |
422 | Campaign content uses merge tags that don’t exist. |
not_editable |
409 | The campaign isn’t in a state that allows this (not a draft, still sending, or content from another editor). |
editor_chosen |
409 | A concurrent request chose another editor for the draft. |
recently_sent |
409 | A campaign sent within 30 days can’t be deleted yet. |
not_active |
409 | The workflow is a draft (pause) or isn’t active (add subscribers). |
list_timeout |
503 | The list took too long. |
Treat unknown codes by their HTTP status: new codes may be added.
Rate limits
Section titled “Rate limits”| Bucket | Limit | Counts |
|---|---|---|
| General | 120 requests a minute per token | Every authenticated request, including batch envelopes and each request in a batch. |
| Import | 5 a minute per token | POST /groups/{id}/import-subscribers, which also counts in the general bucket. |
The limit is per calendar minute (it resets at the start of each minute), per token. Answers that count carry:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
The bucket’s limit. On an import answer it shows the import bucket. |
X-RateLimit-Remaining |
Requests left in this minute. |
Retry-After |
On 429 only: seconds until the next minute. |
HTTP/1.1 429 Too Many RequestsContent-Type: application/jsonRetry-After: 18X-Ratelimit-Limit: 120X-Ratelimit-Remaining: 0
{"error":{"code":"rate_limited","message":"Too many requests with this API token. Wait a minute and try again, or send several requests in one batch."}}Answers that don’t count have no rate limit headers: a failed authentication, a request from a refused IP address, and GET /openapi.yaml.
Good practice: read X-RateLimit-Remaining, wait Retry-After seconds after a 429, use batch requests and the bulk import for many changes, and use separate tokens for separate systems (each token has its own limits).
Other limits
Section titled “Other limits”| What | Limit |
|---|---|
| Request body | 1 MB; bulk import 10 MB |
Page size (limit) |
1 to 100, default 25 |
| Subscriber activity page | 50 events |
| Bulk import | 1 to 50,000 rows per request |
| Batch | 1 to 50 requests |
| Add subscribers to a workflow | 1 to 100 per request |
| Test emails | 1 to 5 addresses per request; 50 per workspace in any 24 hours |
| Webhooks | 20 per workspace; up to 100 events per batched delivery; 10 seconds to answer |
| API tokens | 50 active per workspace; 20 IP addresses or ranges per token |
| Names | Webhook and token names up to 100 characters; campaign name and subject up to 255 |