Errors & limits
What an error looks like, the rate limit, paging through lists, and what each plan allows.
Errors
Errors use standard HTTP status codes and always the same JSON body:
{
"error": {
"code": "plan_required",
"message": "Creating your own overlays requires the Pro plan or higher.",
"details": { "requiredPlan": "pro" }
}
}code is stable and meant for your code; message is for humans and may change. details is there when there's more to say.
| Status | code | When |
|---|---|---|
| 400 | validation_error | The body or query failed validation. details is a list of { path, message }. |
| 400 | bad_request | Malformed JSON, nothing to update, or an invalid parameter. |
| 401 | unauthorized | No credentials were sent. |
| 401 | invalid_token | The key is unknown, revoked or expired. |
| 403 | insufficient_scope | The key lacks a required scope (details.requiredScope). |
| 403 | plan_required | Your plan doesn't include this (details.requiredPlan). |
| 403 | limit_reached | A plan limit was reached (details.limit). |
| 403 | forbidden | Not allowed, for example an API key on a website-only endpoint. |
| 403 | account_disabled | The account is suspended. |
| 404 | not_found | Doesn't exist, or isn't yours. (Other people's resources are never revealed.) |
| 409 | conflict | The overlay changed since your baseRev (details.currentRev). |
| 413 | payload_too_large | The body or overlay document is too big. |
| 415 | unsupported_media_type | Send Content-Type: application/json. |
| 429 | rate_limited | Too many requests, see below. |
| 500 | internal_error | Our side. The response never contains internals; try again later. |
Rate limits
API keys get 10 requests per minute per account, on every plan (Free, Pro and Ultimate). All of an account's keys share that one limit, so more keys don't mean more requests. The website and the desktop app have their own, higher limits.
We expect to raise the paid-plan limits later, so read yours from plan.entitlements.apiRequestsPerMinute in GET /me instead of hard-coding 10.
Every response to a key-authenticated request tells you where you stand:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per window |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Unix time (seconds) when the window resets |
Go over and you get 429 with a Retry-After header (seconds). Wait that long before retrying, and don't retry in a tight loop.
HTTP/1.1 429 Too Many Requests
Retry-After: 41
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1791525999Pagination
- Inventory uses a cursor: pass
limit(1–100, default 25) and, for the next page, thenextCursoryou got back ascursor. AnextCursorofnullmeans you're done. - Catalog uses an offset:
limit(1–100, default 50) andoffset. The response givesnextOffset, ornullat the end.
What your plan allows
The API applies your current plan on every request, never anything cached in a key.
| Free | Pro | Ultimate | |
|---|---|---|---|
| Add predefined overlays your plan allows | Yes | Yes | Yes |
| Create, update, fork overlays | No (plan_required) | Yes (max 25) | Yes (max 200) |
| Inventory size | 50 | 200 | 1,000 |
| Overlays that require a higher plan | locked | locked unless Pro | unlocked |
| Marketplace widgets, overlays and themes | Free ones | Free and Pro ones | All |
A theme needs the plan of its most demanding part.
If an account is downgraded, the overlays it created stay stored but come back as locked: true and can't be read or edited (403 plan_required). They can still be deleted.

