Skip to content
Try it free

API

API errors and limits

The error object, HTTP statuses, every error code with its CLI exit code, rate limits, quotas, upload limits and how to retry safely.

View as Markdown
On this page

Errors come in two shapes. A request that is refused gets an HTTP error with a snake_case code. A job that fails later carries the engine's error, with an upper-case code. The same upper-case codes are used by the CLI and the MCP server. A code that means "out of minutes" in one place means it everywhere.

The error object

A refused request

A request that cannot be accepted gets an HTTP status and this body:

JSON
{  "error": {    "code": "source_auth_required",    "message": "Instagram needs a login to read this video.",    "engineCode": "SOURCE_AUTH_REQUIRED",    "hint": "Download the video or its audio yourself and upload the file instead.",    "retryable": false,    "stage": "resolve"  }}
FieldDescription
codeA stable snake_case code. Branch on this. The table below lists them.
messageA sentence for a person. Do not parse it.
engineCodeThe engine's code, when the problem is one of those in Error codes.
hintWhat to do next, when there is one. Written to be shown to users.
retryable, stageAs in a job's error, when the engine raised it.

The hint in this example tells people to download the file and upload it.

Some errors carry more fields: minutesNeeded and minutesRemaining on quota_exceeded and anonymous_limit, and upload_id when a multipart request was refused after the file arrived. A Retry-After header says how many seconds to wait on a 429 or 503.

A failed job

A job that fails, and the error event that ends its stream, carry the engine's error:

JSON
{  "code": "SOURCE_BLOCKED",  "message": "YouTube refused the download (HTTP 403).",  "retryable": true,  "scope": "job",  "stage": "download",  "hint": "Update yt-dlp: brew upgrade yt-dlp"}
FieldDescription
codeA stable, upper-case code. Branch on this.
messageA sentence for a person. Do not parse it.
retryableWhether trying the same request again can help.
scopejob when only this input failed. account when the account has a problem, such as no minutes. account errors will repeat for every job until someone acts.
stageWhere it happened: resolve, download, extract, transcribe, synthesize and the others in The JSON protocol.
hintWhat to do next. Written to be shown to users.
status, retryAfterMsThe upstream HTTP status and wait, when there was one.

For a job, the error is in error on GET /v1/jobs/:id. A canceled job has the code CANCELLED. Error messages never contain keys or credentials. When Scribiz's own connection to the model service fails or runs out of quota, you see INTERNAL or RATE_LIMIT with a hint to try again, never an account problem of yours.

HTTP statuses

StatusCodeMeaningWhat to do
400invalid_requestThe body, a field or an id is not validFix the request. The message says what.
400length_mismatchThe upload was shorter than its Content-LengthSend it again
401invalid_api_key, unauthorizedNo key, or a bad oneFix the key
402quota_exceededOut of minutesAdd minutes, or wait for the next period
403insufficient_scope, session_requiredThe key's scope or kind does not allow itUse a key with the right scope, or a session
403account_required, turnstile_required, turnstile_failedAnonymous use does not allow itSign in, or send a Turnstile token
403not_ownerOnly the creator of a job can cancel or delete itDo not retry
404not_found, upload_not_foundNo such job or upload, or it belongs to someone elseDo not retry
409job_not_ready, upload_in_use, key_limit_reachedThe thing is in the wrong stateWait, or fix the state
410job_expired, upload_expiredThe result or the upload has expiredRun it again
411length_requiredAn upload needs a Content-LengthSend one
413video_too_long, body_too_largeOver the length limit of the plan, or the upload is over 100 MBSend less
415unsupported_media, unsupported_media_typeThe file is not media Scribiz can readSend another file
422url_unsupported, source_unavailable, source_live, source_auth_required, input_unsupported, idempotency_key_reused and othersThe link or the input cannot be usedFix the request. A source error carries engineCode and a hint.
429rate_limited, anonymous_limit, too_many_in_flight, too_many_uploads, too_many_streamsA limit was reachedWait for Retry-After, then retry
451blocked_contentThe video was removed after a takedown requestDo not retry
500, 502, 503, 504internal_error, server, network, timeout, busy, not_configured and othersSomething failed on our side or upstreamRetry with a growing pause
501billing_not_configuredThis is not open for purchase on this serverDo not retry

A source error from the engine becomes the lower-case form of its code with the engine code beside it: source_unavailable with engineCode: "SOURCE_UNAVAILABLE". Its status is 422 for a problem with the link, 413 for a video that is too long, and 502 to 504 for a problem upstream.

Error codes

These are the engine's codes. Jobs, the event stream, the CLI and MCP use them as written. The API's own refusals use the snake_case form with the engine code beside it. Every code, whether a retry can help, and the exit code the CLI uses. See Commands for the exit codes.

The input

CodeMeaningRetryExit
INPUT_NOT_FOUNDThe file or upload does not existNo4
INPUT_UNSUPPORTEDThe file cannot be read as audio or videoNo4
INPUT_CORRUPTThe file is damagedNo4
NO_AUDIO_STREAMThere is no audio. Watch can still runNo4
URL_UNSUPPORTEDThe link is not one Scribiz can useNo4

The source

CodeMeaningRetryExit
SOURCE_UNAVAILABLEPrivate, deleted or restricted in a regionNo4
SOURCE_AUTH_REQUIREDThe site needs a loginNo4
SOURCE_BLOCKEDThe site refused a server: a 403, a bot checkOnly if another way is left4
SOURCE_LIVEA live streamNo4
SOURCE_TOO_LONGOver your plan's length limitNo4
TOOL_MISSINGffmpeg, ffprobe or yt-dlp is not installedNo6

The service

CodeMeaningRetryExit
AUTHThe credential was rejectedNo3
QUOTAOut of minutes, or the model service's quota is used upNo3
RATE_LIMITSlow down. Honor Retry-AfterYes5
SERVERThe model service had an errorYes5
NETWORKA connection failedYes5
TIMEOUTA request took too longYes5
TOO_LARGEA request body was over a limitNo5
CONTENT_BLOCKEDThe model service declined to process the contentNo5
PROVIDER_PROTOCOLThe model service answered in a way Scribiz did not expectNo5
INCOMPLETE_OUTPUTPart of the answer was missing. Scribiz repairs it itself, and only reports INCOMPLETE_COVERAGE if a gap remainsYes5

The run

CodeMeaningRetryExit
CANCELLEDYou or the host canceled the runNo130
COST_LIMITThe estimate was above --max-costNo1
INTERNALA bug. Please report itNo1

Retrying safely

  • Retry 429, 5xx, timeouts and dropped connections. Do not retry other 4xx responses: the same request will fail the same way.
  • Honor Retry-After on a 429 or 503. Without it, wait 1, 2, 4 and 8 seconds with a little random jitter, and stop after a few tries.
  • Send an Idempotency-Key with POST /v1/context. Then a retry cannot create a second job or a second charge.
  • A job that failed with retryable: true can be started again with a new request. A failed job keeps its audio for the retry, so work that already finished, such as audio pieces that were transcribed, is not paid for twice.
  • Scribiz retries on its own, inside a job, for network errors, timeouts and rate limits. A job reports failed only after that.
  • A job that is interrupted, for example by a restart of the service, is queued again on its own, up to three times. Its stream shows a requeued event.

Limits

LimitAnonymousFreePro
Listen and Watch minutes10 per day30 per month600 per month
Caption jobs30 per day0.1 minutes per minute of video, from your minutes0.1 minutes per minute of video, from your minutes
Questions5 per day0.1 minutes each0.1 minutes each
Longest video15 minutes2 hours6 hours
Running jobs at once122
Jobs waiting at once133
Result kept for24 hours30 days30 days

Mac Lifetime accounts have the free plan's limits and a one-time 300 minutes.

LimitValue
Requests that create work (jobs, uploads, questions)6 per minute anonymous, 30 per minute for an account
Upload size100 MB
An upload is kept1 hour
Uploads waiting to be used3 anonymous, 10 for an account
Event streams open at once8
Event stream keep-aliveEvery 15 seconds
Body of POST /v1/context64 KB
Jobs waiting across the service200. The next is 503 with busy.
API keys per account10
Concurrent MCP runs per key2

Plan limits are the same ones shown on the pricing page and in Minutes and billing. For a larger input, cut the audio yourself and upload that, or use the CLI, which cuts the audio on your machine. With your own Gemini key it has no limit from Scribiz. Signed in to an account, it uses that account's minutes.

Usage and metering

Hosted jobs are charged in minutes of source video times the multiplier of what ran: captions 0.1 (and, without an account, 30 lookups a day), Listen 1, Watch 1, Both 2, a question 0.1, a stored result 0. The charge is made when the job ends, from what ran. A canceled or failed job is not charged. Two callers who share one run of the same video each pay the minutes, and the upstream cost is recorded once.

Scribiz measures usage from what the model service reports it processed, never from a number the client sends. So there is no header to set, and no way to under-report. See Minutes and billing.

When the hosted tool is busy

If anonymous use has spent the day's budget, Listen and Watch are switched off for anonymous callers for a while. A request that needs them answers 503 with busy, and Auto falls back to captions when captions exist: the 202 carries degraded. Captions keep working. Signed-in accounts are not affected. GET /v1/status shows the state under free_tier.listen_watch. The MCP server without a key has its own daily limit across all callers. It cannot use up the web tool's, and the web tool cannot use up its.

When the queue is full, or the disk is nearly full, new jobs and uploads also answer 503 with busy. Retry after the Retry-After wait.

Checked against the Scribiz build on 2026-10-05.

Loading the index