Responses & errors

One envelope for everything, so a client parses success and failure the same way.

The envelope

success
{
  "success": true,
  "message": "Video added.",
  "data": { "asset": { "public_id": "oI1j0SfJK3v" } },
  "meta": { "timestamp": "2026-08-17T20:36:23.813Z" }
}
failure
{
  "success": false,
  "message": "Invalid or expired API key.",
  "error": { "code": "INVALID_API_KEY" },
  "meta": { "timestamp": "2026-08-17T20:36:23.813Z" }
}

Branch on success. Use error.code for logic — message is written for humans and may be reworded without notice.

Error codes

By status

KeyTypeSampleDescription
INVALID_API_KEYoptional401Invalid or expired API key.Missing, malformed, unknown, revoked or expired — all the same response, on purpose.
PERMISSION_DENIEDoptional403error.details = ["add_video"]The key’s organization lacks a permission. details lists the missing codenames.
API_KEY_EXISTSoptional409“Production server” is already active.One key is live per organization. Revoke the current one before creating another.
NOT_FOUNDoptional404Video not found.Does not exist, or is not yours. Deliberately indistinguishable.
VALIDATION_ERRORoptional422error.details = { "title": "Enter a title" }The body failed validation. details is per field.
RATE_LIMITEDoptional429Retry-After: 37Too many requests. The header says how long to wait.
TRANSCODE_NOT_CONFIGUREDoptional409This organization has no transcoding settings yet.Your encoding settings have not been set up. Nothing is created — talk to the StreamOS team and it is a one-minute fix. Retrying will not help.
DRM_NOT_AVAILABLEoptional409Your plan does not include DRM.Sent drm_enabled: true on a video or stream, but your plan does not include DRM. Retrying with the same body will not help — either omit drm_enabled or ask the StreamOS team to enable DRM for your organization.
TRANSCODE_QUEUE_FAILEDoptional502Transcoding service timed out.We reached the pipeline and it refused or timed out. Safe to retry.
NO_SLOTS_AVAILABLEoptional503No stream slots available.Live stream capacity is exhausted. Retry shortly.
A 404 never confirms that something exists somewhere else. An id belonging to another organization returns exactly what a typo returns — otherwise the API would be a way to test whether an id is real.

Rate limits

Limits are per key, so running your integration across several servers does not buy more allowance, and another customer cannot consume yours.

Limits

KeyTypeSampleDescription
POST /livestreams/optionalper organization10 / minuteStream slots are a finite shared pool.
All endpointsoptionalper key120 / minuteThe general ceiling for a single API key, counted per key rather than per IP.
POST /videos/optionalper organization20 / minuteEach success queues a billed transcode, so this is capped for the org across every key and the dashboard together.
POST /videos/upload-url/optionalper organization60 / minutePresigning is cheap, but it is the door to the bucket.

On 429, read Retry-After and wait that many seconds. Retrying immediately just consumes the next window.

What is safe to retry

5xx and 429 are safe — nothing was created. A 4xx other than 429 will fail identically until you change the request.

POST /videos/ is idempotent on source_url: registering the same URL twice returns the existing asset rather than transcoding it again.