Skip to content

Ask AI

Ask a question about Scribewren, such as uploading a file, editing a transcript, plans or the API.

Ctrl or Cmd+Enter to send

Answers are written by AI and can be wrong. For account or billing help, use the chat or contact us. Contact support (opens in a new tab)

Errors

Errors use the same envelope as successful responses. success is false, data is null, and error explains what went wrong.

{
"success": false,
"code": 403,
"message": "You have reached your plan limit.",
"data": null,
"error": {
"code": "PLAN_LIMIT_EXCEEDED",
"details": { "limit": "concurrency", "max": 5, "used": 5 }
},
"meta": {
"requestId": "019a4f30-1c22-7a10-b5d3-42f1c0a8e777",
"timestamp": "2026-12-01T09:41:07.512Z"
}
}

The numbers in details are illustrative.

  • code repeats the HTTP status.
  • error.code is the stable, machine-readable identifier in UPPER_SNAKE_CASE. Branch on this and on the HTTP status.
  • message is for people. Its wording can change, so never match on it.
  • error.details is an object. Its keys depend on the code and are listed below. It can be empty.
  • meta.requestId identifies the request. Include it when you contact support.

Error messages never contain transcript text, titles or file names.

400 VALIDATION_ERROR lists every invalid field in error.details.fields:

{
"error": {
"code": "VALIDATION_ERROR",
"details": {
"fields": [
{ "field": "filename", "reason": "required" },
{ "field": "options.language", "reason": "invalid_enum" }
]
}
}
}

reason is one of required, invalid, too_short, too_long, out_of_range, invalid_enum or unknown_field. Some endpoints use more specific values, such as invalid_boolean on Export. Treat an unfamiliar reason as a generic invalid value.

A body field the API does not know is unknown_field. The API does this on purpose, so a misspelled option does not pass silently.

Status error.code Meaning error.details
400 VALIDATION_ERROR A field or query parameter is missing or invalid. fields: a list of { field, reason }.
400 FILE_TOO_LARGE The file is above the plan’s size limit. maxBytes.
400 UNSUPPORTED_MEDIA_TYPE The file type is not supported. extension, contentType.
400 UPLOAD_INVALID The parts you sent do not match the upload. reason: part_count_mismatch, invalid_parts, missing_object or size_mismatch.
401 UNAUTHENTICATED The API key is missing, malformed, unknown or revoked. Empty. The response has WWW-Authenticate: Bearer.
403 FEATURE_NOT_IN_PLAN The owner’s plan does not include the API. feature, requiredPlan.
403 PLAN_LIMIT_EXCEEDED A plan limit blocks the request. limit, max, used, and resetAt when the limit resets.
403 ACCOUNT_RESTRICTED The account can read and export, but cannot start new processing. Empty.
403 ACCOUNT_SUSPENDED The account is banned or is being deleted. reason: banned or deletion_pending.
404 NOT_FOUND The resource does not exist, belongs to another account, or the path is not served on this host. Empty.
405 METHOD_NOT_ALLOWED The path exists but not for this HTTP method. Empty.
409 INVALID_STATE_TRANSITION The resource is not in a state that allows this action. reason: not_pending, not_ready or upload_expired, or status and action.
410 MEDIA_EXPIRED The original file was already deleted. object: original.
413 PAYLOAD_TOO_LARGE The request body is larger than the endpoint accepts. Empty.
415 UNSUPPORTED_MEDIA_TYPE The request Content-Type is not application/json. Empty.
429 RATE_LIMITED You are sending requests too fast. policy, retryAfterSeconds. See Rate limits.
500 INTERNAL_ERROR An unexpected failure on our side. Empty.
503 EXPORT_FAILED A file could not be generated. format.
503 SERVICE_UNAVAILABLE A dependency or the service is not available right now. reason: uploads_paused, rate_limiter_unavailable, database_unavailable or maintenance.

UNSUPPORTED_MEDIA_TYPE appears twice because it covers two cases. With status 400 the uploaded file’s type is not accepted. With status 415 your request body was not sent as JSON.

PLAN_LIMIT_EXCEEDED is the only code for limits that come from the plan. details.limit says which one. On this API you will mostly see concurrency, which means too many uploads and jobs are open at once. max is the plan’s value and used is where you are. When the limit resets at a fixed time, resetAt is that UTC time. It is left out for concurrency, which frees up as jobs finish.

Read the current values from Usage instead of hardcoding them.

A transcription that ended as failed carries its own errorCode (MEDIA_UNREADABLE, NO_AUDIO_STREAM, INTERNAL_ERROR or WORKER_TIMEOUT). That is a property of the job and arrives with a normal 200 response. It is not an error.code. See Transcriptions.

Failure Retry? What to do
429 RATE_LIMITED Yes Wait for the Retry-After header, then retry.
503 SERVICE_UNAVAILABLE, 503 EXPORT_FAILED Yes Retry with exponential backoff and a little random jitter. For uploads_paused, start with a longer wait.
500 INTERNAL_ERROR Yes, a few times Back off. If it keeps happening, contact support with the requestId.
Network timeout or dropped connection Yes, with care GET requests are safe to repeat. For POST /media, check GET /media?status=pending_upload first, so you do not create a duplicate. POST /media/{id}/complete is safe to repeat.
401, 403, 404, 405, 409, 410, 413, 415 No The request or the account state must change first.
400 No Fix the request. Read error.details.

Log the requestId of every failed call, and nothing from the request body.

Can't find what you need? Contact support, email [email protected], or open Help and then Contact in the app. scribewren.com