Overview
API conventions
Errors
Every error answers in one shape:
{
"success": false,
"errorCode": "INVALID_DPI",
"error": "dpi must be between 1 and 1200.",
"details": [
{
"field": "dpi",
"code": "INVALID_DPI",
"message": "dpi must be between 1 and 1200.",
"params": { "min": 1, "max": 1200 }
}
],
"requestId": "4bf92f3577b34da6a3ce929d0e0e4736"
}successbooleanfalse on every error.
errorCodestringThe code to branch on. Every error has one.
errorstringA sentence for people — in English when the request carries an X-Api-Key header. It may be reworded — do not match on it.
detailsobject[]Errors only: what went wrong, field by field.
Show child attributesHide child attributes
fieldstringThe request field at fault, when there is one.
codestringThe error code for this field.
messagestringA sentence for people.
paramsobjectThe numbers in the message (min, max, retryAfter…), to build your own sentence.
requestIdstringErrors only: the same value as the X-Request-Id header.
Every code, with its HTTP status, its English message and whether a retry can help, is listed by GET /v2/errors (no key needed) — and on Error codes.
Request ID
Every response carries an X-Request-Id header; an error repeats it as requestId. Quote it when you report a problem — it finds your call in our logs.
Rate limits
6,000 requests per minute per API key; uploading reference images (POST /images/presign) at most 30 a minute. Every response says where you stand:
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | Requests allowed per minute |
| X-RateLimit-Remaining | Requests left in this minute |
| X-RateLimit-Reset | When the count starts again, in Unix seconds |
| Retry-After | On a 429 only: seconds to wait |
Over the limit, the call answers 429 with errorCode RATE_LIMITED. Wait Retry-After seconds, then send it again.
Pagination
Lists take page (from 1) and pageSize — limit is accepted as the same thing. The total is in the X-Total-Count header.
| List | Default pageSize | Max | No page or pageSize sent |
|---|---|---|---|
| GET /characters | 50 | 200 | Everything |
| GET /user-assets | 50 | 200 | Everything |
| GET /jobs | 10 | 100 | The first 10 |
| GET /account/transactions | 20 | 100 | The first 20 |
GET /jobs gives its total in the body (total) instead of the header.
Idempotency
POST /images/generations and POST /publishing/posts take a key, in the Idempotency-Key header or the idempotencyKey body field, kept for 24 hours:
| You send | You get |
|---|---|
| The same key, the same body | The earlier job or post, with idempotentReplay: true — nothing made or charged again |
| The same key, another body | 422 IDEMPOTENCY_KEY_REUSED |
| Header and body field with different keys | 422 VALIDATION_ERROR |
| No key | A new job or post, every time |
IDs
Every id is an opaque string with a prefix — sa_… (a social account), pp_… (a post’s network), tx_… (a transaction), acct_… (an account). Store it and send it back as it is; do not compare, sort or compute on it.