reference
Errors & status codes
Every /v1 endpoint returns errors in one envelope, so you write the
error-handling path once and it works for the whole API:
{
"error": {
"code": "insufficient_scope",
"message": "the API key is missing the required scope: stream:golive",
"requestId": "req_9f2c1a7b4e5d6f80",
"requiredScope": "stream:golive"
}
}
code— a stable, machine-readable string. Branch on this, not on the HTTP status or the human message. Codes are part of the compatibility promise (see Versioning); new codes may be added, existing ones will not change meaning within/v1.message— a human-readable explanation. For logs and developers, not for branching. The wording may change at any time.requestId— the same value returned in theX-Request-Idresponse header. Quote it when you ask us to look into a request.requiredScope— present only oninsufficient_scope. It names the exact scope your key is missing, so you can request a key that has it.
The X-Request-Id header
Every response — success or error — carries an X-Request-Id. If you send your
own X-Request-Id on the request, we echo it back; otherwise the gateway
generates one (req_ + hex). Log it on your side and you can correlate any call
with our logs.
Codes and their HTTP status
| HTTP | code |
When it happens | What to do |
|---|---|---|---|
400 |
invalid_request |
Malformed input: a bad cursor, a non-numeric limit, a body that doesn't parse. |
Fix the request. Don't retry unchanged. |
401 |
invalid_key |
No key sent, or the key is unknown, revoked, or expired. | Check the Authorization: Bearer sk_live_… (or X-API-Key) header; mint a new key if needed. |
403 |
insufficient_scope |
The key is valid but lacks the scope for this route. requiredScope names it. |
Issue a key that holds the named scope. |
404 |
not_found |
The studio (or sub-resource) doesn't exist, or your account can't see it. | Verify the id. A resource you don't own reads as not-found. |
429 |
rate_limited |
You exceeded the per-key rate limit. | Back off for the Retry-After seconds, then retry. See Rate limits. |
503 |
write_disabled |
A write endpoint (go-live / go-offline) is currently gated off. |
Not retryable by you; the write surface is disabled in this preview. |
503 |
unavailable |
A dependency needed to serve the request is temporarily down (e.g. key verification). | Transient — retry with backoff. |
502 |
bad_gateway |
An upstream service returned an unexpected error. | Transient — retry with backoff. |
500 |
internal |
An unexpected error on our side. | Retry with backoff; if it persists, send us the requestId. |
How to handle errors
- Read
error.code, not the HTTP status alone. Two different503s (write_disabledvsunavailable) mean very different things — one is a deliberate gate, the other is transient. - Retry only the transient codes (
unavailable,bad_gateway,internal, andrate_limitedafter itsRetry-After). Use exponential backoff. Never hot-loop a retry. - Never retry a
4xxunchanged —invalid_request,invalid_key,insufficient_scope, andnot_foundwill fail identically until you change the request or the key. - Surface
requestIdin your own error reports. It's the fastest way for us to trace a failing call.
Auth errors specifically
- A 401
invalid_keymeans the credential itself is the problem (missing, unknown, revoked, expired). Re-check the header and the key. - A 403
insufficient_scopemeans the credential is fine but under-scoped.requiredScopetells you what to add. A key can only ever act within its owner's own account, so a 404 rather than a 403 can also mean "not yours".
See the full request/response shapes in the API reference and the credential model in Authentication.