The error shape
When a request fails, the API replies with the matching HTTP status and a small JSON body. The error field is a stable, machine-readable token; an optional message adds human-readable detail:
{ "error": "forbidden" }
{ "error": "bad_request", "message": "missing id/name" }
Branch on the status code and the error token, not on message.
Status codes
| Code | Meaning |
|---|---|
200 / 201 | Success. 201 is returned when a resource was created. |
204 | Success with no body (for example a delete, or a folder move). |
400 | The request was malformed or failed validation. For a changeset in the line form, parse_error names the line and column. Also the answer when no credential was sent, or the bearer is not an API token: bad_request, with a message beginning authentication required. |
401 | invalid_token: an lwt_ token that is unknown, expired, or revoked. sign_in_required: an action that needs a signed-in session (for example changing a team), refused to a token. |
403 | Authenticated, but not allowed to touch this resource. A write with a read-only token answers read_only_token. |
404 | No such resource (or it is hidden from you, which reads the same). |
405 | use_changesets: tokens change tabs with changesets, not a whole-tab PUT. |
409 | A conflict, such as a folder cycle, deleting a canvas's only tab with a token (last_tab). For a changeset: changeset_conflict (an element changed since your base), elements_held (somebody has it selected) or tab_busy (the tab kept changing; read it again). |
410 | document_trashed: the canvas is in the Trash. Restore it (POST /trash/{id}/restore) to use it again. teams_moved or tokens_moved: teams and API tokens are managed on liveworkspace.app; manageAt says where. |
412 | stale_tab: a changeset sent with "strict": true on a tab that moved since its base. |
413 | The request body is over the size cap. |
422 | A changeset the edit operations refuse (an element nothing matches, a field the element does not have, a frame that would take in what you did not name, a test that found another value); text says what and how to fix it. |
429 | Rate limited. Slow down and retry. |
5xx | A server-side error. Safe to retry idempotent reads after a pause. |
Rate limits
The API is rate limited to keep one client from starving others. Writes (POST / PUT / DELETE) and reads under a token each have their own budget, keyed on the token, so a runaway integration is throttled independently of your interactive use of the editor. Over the budget, a request returns 429; back off and retry.
On the hosted service the budgets are 300 writes a minute and 120 token reads a minute, per token. A few endpoints carry their own tighter limit: 30 share-code attempts a minute (which is also the brute-force guard on a share password), 60 link unfurls, and 20 AI requests, each counted per IP rather than per token.
Treat 429 as routine for bulk work: pause and retry rather than hammering. Spread large imports
or backups out instead of firing every request at once.
Payload size
Request bodies are capped at 8 MB (image uploads have their own cap), and one tab's data at about 1.9 MB. A body that declares a larger size is rejected with 413 payload_too_large before it is read; an oversized tab or changeset is rejected with 413 when it is checked. If you hit this, split the work across more, smaller tabs or canvases.
Token expiry and revocation
Every API token lasts six months, then expires. After that, requests with it fail with 401 invalid_token, the same as a revoked token. There is no never-expires option, so build rotation in: create a fresh token before the old one lapses, update your integration, then revoke the old one.
You can hold up to 20 live tokens at once, across every liveworkspace app. Tokens are made and revoked on liveworkspace.app.
Revocation takes effect immediately. The next request a revoked token makes is rejected, so revoke any token you suspect has leaked and replace it.
Was this article helpful?