Get started
Errors & rate limits
Every response is wrapped in a consistent envelope:
Success
{
"success": true,
"data": { /* ... */ }
}Error
{
"success": false,
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "This credential is missing the render:write scope",
"path": "/v1/embed/emb_123/render",
"timestamp": "2026-09-08T12:00:00.000Z"
}Authentication and authorization — thrown before your request reaches any handler:
| Param | Type | Notes |
|---|---|---|
| 401 NO_SESSION | No credential was sent at all. | |
| 403 INVALID_API_KEY | An fl_live_… key is unknown or revoked. | |
| 403 SUBSCRIPTION_SUSPENDED | The key's workspace subscription is suspended. | |
| 403 WORKSPACE_MISMATCH | The request targets a workspace other than the key's own. | |
| 403 INSUFFICIENT_SCOPE | An embed session or MCP connection is not allowed to use this endpoint. API keys have full access and never get this. | |
| 403 RLS_DENIED | A database policy blocked the query — surfaced only if a guard check is ever bypassed; treat as a bug report. |
Credits and plan limits:
| Param | Type | Notes |
|---|---|---|
| 402 INSUFFICIENT_CREDITS | The workspace's subscription has run out of render credits. | |
| 402 WORKSPACE_CREDIT_LIMIT | The workspace's own per-workspace credit limit was hit, ahead of the shared subscription pool. | |
| 403 PLAN_FEATURE_NOT_INCLUDED | The current plan doesn't include the feature being used (e.g. social publishing, embeds). |
Rendering:
| Param | Type | Notes |
|---|---|---|
| 422 RENDER_TOO_LARGE | The rendered output exceeds the maximum file size. | |
| 422 VIDEO_TOO_LONG | The computed animation duration exceeds the maximum allowed video length. | |
| 400 INVALID_VIDEO_OPTIONS | An fps, scale or quality value for an animated render is out of range. | |
| 400 INVALID_MODIFICATIONS | A modification value is not a string. | |
| 503 QUEUE_UNAVAILABLE | The render queue is unreachable — retry shortly. | |
| 504 RENDER_TIMEOUT | The render job exceeded its wait timeout. |
Render editing / embed sessions:
| Param | Type | Notes |
|---|---|---|
| 403 EDIT_SESSION_REQUIRED | A render-mode route was called without an edit session token. | |
| 409 EDIT_SESSION_NOT_OPENED | A draft or save was requested before the edit session was opened. | |
| 409 EDIT_SESSION_ALREADY_OPENED | An editor link was used twice — create a new edit session. | |
| 403 EMBED_MISMATCH | An embed session token was used against a different embed id. | |
| 403 DOMAIN_NOT_ALLOWED | An edit session's origin is not on the embed's allowed domains. | |
| 400 PAGE_CHANGES_NOT_ALLOWED / LOCKED_LAYER_CHANGED | An editor save broke the session's allowPageChanges / allowUnlock option. Full list under Render editing → Errors. |
Validation and Postgres conflicts:
| Param | Type | Notes |
|---|---|---|
| 400 (no code) | Request body failed validation — unknown fields are rejected outright, and message is an array of every failed field, not a single string. | |
| 429 (no code) | Rate limit exceeded — see below. | |
| 409 (unique constraint) | Postgres conflict — code is the violated constraint's name. |
A
code field is only present when the thrown error explicitly set one. Plain validation failures and rate-limit responses carry a message but no code — match on statusCode for those.Rate limits scale with your plan (requests/minute) and are reported back on GET /v1/me under plan.limits.rate_per_minute. Exceeding it returns 429 with no code field:
429 Too Many Requests
{
"success": false,
"statusCode": 429,
"message": "ThrottlerException: Too Many Requests",
"path": "/v1/templates/tpl_123/render",
"timestamp": "2026-09-08T12:00:00.000Z"
}The rate-limit bucket depends on how the request is authenticated: per workspace for an API key or MCP connection, per user for a dashboard session, and per IP address for an embed session. One noisy integration can't exhaust a teammate's allowance.
Was this page helpful?