API reference
Errors

Errors

Errors are returned as JSON with the HTTP status code carrying the category. Most bodies have an error field naming the class of problem and a message field written for a person. Some add fields that let you recover without guessing.

400 Malformed JSON

The body could not be parsed.

{
  "error": "Invalid JSON",
  "message": "Request body must be valid JSON"
}

400 Failed validation

The body parsed but did not match the schema. details names every field that failed, so you can map errors back to your form or payload.

{
  "error": "Validation error",
  "message": "Invalid request parameters",
  "details": [
    { "field": "inputUrl", "message": "Invalid input URL" },
    { "field": "preset", "message": "Invalid input" }
  ]
}

Invalid input means the field was missing entirely. Invalid input URL means the value was present but is not a valid absolute URL.

400 Unknown preset

The preset identifier does not exist. Every valid identifier is listed, so you never have to look one up to recover.

{
  "error": "Invalid preset",
  "message": "Unknown preset: nope",
  "availablePresets": [
    "mp4_264_240p", "mp4_264_480p", "mp4_264_720p", "mp4_264_1080p",
    "mp4_265_1080p_efficient", "hls_abr_264_basic", "hls_abr_264_standard"
  ]
}

Despite the field name, availablePresets here is every preset the service knows, not the subset your plan can use.

400 Operation not allowed in this state

Returned by cancellation when the job has already finished.

{
  "error": "Invalid operation",
  "message": "Cannot cancel job with status: completed. Only queued or processing jobs can be cancelled."
}

401 Unauthorized

Missing header, malformed header, unrecognised prefix, unknown key, or revoked key. All five produce the same body.

{
  "error": "Unauthorized",
  "message": "Valid API key required. Use format: Bearer spk_xxx or Bearer sk_xxx"
}

Not retryable. Check the key rather than the request.

403 Preset not on your plan

The preset exists, but your plan does not include it. This body tells you what you can use instead and what you would need to upgrade to.

{
  "error": "PRESET_NOT_ALLOWED",
  "message": "The \"hls_abr_265_efficient_4k\" preset is not available for your Starter plan",
  "code": "PRESET_NOT_ALLOWED",
  "availablePresets": [
    "mp4_264_240p", "mp4_264_480p", "mp4_264_720p", "hls_abr_264_basic",
    "audio_aac_podcast", "util_thumbnails", "util_subs_sidecar_basic"
  ],
  "requiredPlan": "enterprise",
  "yourPlan": "free",
  "upgradeMessage": "Upgrade to enterprise plan to access this preset"
}

Here availablePresets really is your plan's list. Note that yourPlan is the plan identifier while the message uses the display name, so a free account reads free in one field and Starter in the other.

404 Not found

The job does not exist, or it belongs to another account. The two cases are deliberately not distinguished.

{
  "error": "Not found",
  "message": "Job not found or you do not have access to it"
}

410 Output gone

The job completed, but its output can no longer be signed for download. This happens for jobs stored before the current storage layout. The file is not recoverable; run the job again.

{
  "message": "The output for this job is no longer available for download"
}

429 Quota exhausted

You have used every job in this month's allowance. The counter resets at the start of the calendar month.

{
  "error": "Usage limit exceeded",
  "message": "Monthly limit of 10 jobs reached",
  "usage": {
    "current": 10,
    "limit": 10,
    "plan": "Free"
  }
}

Retrying will not help until the month rolls over or you change plan. This is a quota, not a rate limit; there is no per-minute throttle to back off from.

500 Server error

Unexpected. The requestId is a fresh UUID for this failure; quote it when you contact support.

{
  "error": "Internal server error",
  "message": "An unexpected error occurred while processing your request",
  "requestId": "0e2f9c14-7b3a-4d61-b9c8-5a1e7f2d3c40"
}

Safe to retry with backoff.

The download-url exception

GET /jobs/{id}/download-url predates the conventions above and returns a bare message with no error field.

{ "message": "Job not found" }
{ "message": "Output file is not available or job is not completed" }

If you parse errors generically, read message first and treat error as optional.

Failures that are not HTTP errors

A job can be accepted with 201 and still fail later, because the source is only fetched and inspected by the worker. Those failures appear as status: "failed" with a human-readable errorMessage on the job, and they trigger a webhook if you have one configured.

errorMessage containsCauseRetried by the worker
DURATION_EXCEEDEDSource is longer than your plan allowsNo
Download or network wordingSource URL was unreachable or refusedYes, up to 3 attempts
FFmpeg wordingSource could not be decoded with this presetYes, up to 3 attempts

See Handling failures for what to do about each.