transcrevo docs

Errors

The API error envelope and the full code table, from validation and invalid_api_key to insufficient_balance, rate_limited and concurrency_limit.

Errors use the standard envelope:

{
	"error": {
		"code": "validation",
		"message": "The request body failed validation; `fields` maps each invalid field to a stable message key.",
		"retryable": false,
		"requestId": "0f9c2e5a-8f4b-4d61-9a3e-2c7d1b5f0a44",
		"fields": { "url": "url_invalid" }
	}
}
  • message explains what happened in plain text. It is the field to read while integrating, and the one to keep in your logs.
  • code identifies the kind of error and is what your code should branch on: always one of the codes below, never an arbitrary string.
  • retryable says whether repeating the SAME request can succeed. It is the one decision the HTTP status does not answer on its own: a 429 is worth repeating, a 409 from idempotency never will be. Branch on it instead of keeping your own table of which errors deserve a retry.
  • requestId identifies this response, and is also in the X-Request-Id header. Keep it in your logs: it is how we find, on our side, what happened on that exact call.
  • fields (only on validation) points to a stable key per invalid field.

retryable: true does not mean "retry now": on rate_limited the Retry-After header says when, and elsewhere exponential backoff with jitter applies.

Codes

HTTPcodeWhen it happens
400validationInvalid body. See fields.
400chunk_emptyChunk with no content.
400upload_mismatchWhat arrived does not match the expectedBytes declared.
401invalid_api_keyMissing or invalid key in the Authorization header.
402insufficient_balanceNo credits left. Top up in the dashboard.
404not_foundUnknown transcript, or one from another account.
404upload_not_foundUnknown upload, or one from another account.
409upload_busyAnother chunk of this upload is still being written.
409idempotency_conflictIdempotency-Key reused with a different body.
409transcript_processingA transcript still processing cannot be deleted.
409transcript_finishedThe transcript already finished and can no longer be canceled.
409no_webhookThe transcript has no webhookUrl (or has not finished) to replay.
413chunk_too_largeChunk over 16 MiB.
413upload_too_largeFile (or upload) would pass 2 GiB.
429rate_limitedRate limit hit. See the Retry-After header.
429concurrency_limitAccount with no top-up yet already running 2 transcriptions.
500internalOur fault. Retry; contact support if it persists.

The API reference documents, per endpoint, exactly which codes each status can return.

Rate limits

Every response from a limited route carries the three headers, not just the 429:

HeaderWhat it says
RateLimit-LimitThe ceiling for the window
RateLimit-RemainingHow many requests still fit in it
RateLimit-ResetIn how many seconds the next slot opens

They are what lets you slow down BEFORE hitting the ceiling instead of finding it by being refused. 429 rate_limited also carries Retry-After with the real wait, in seconds. The ceilings are per account, on a sliding window:

PathPer minute
POST /v1/transcripts120
POST /v1/uploads120
PUT /v1/uploads/{id}600

A backlog releasing all at once reaches these easily, because a customer's peak looks nothing like their average traffic. Spread the burst over the Retry-After wait instead of retrying immediately; nothing is lost, only delayed. If you need more, talk to us before the migration starts.

Validation keys

FieldKeyMeaning
urlurl_invalidNot a valid http/https URL.
urlurl_or_upload_idSend url or uploadId, exactly one.
uploadIdupload_id_invalidInvalid format.
uploadIdupload_emptyThe upload received no bytes.
languagelanguage_invalidInvalid language code.
speakersspeakers_invalidNot true, an integer from 1 to 20, or "min-max".
idempotencyKeyidempotency_key_invalidLonger than 64 characters.
webhookUrlwebhook_url_invalidNot a reachable public http/https URL.
glossaryglossary_invalidOver 100 entries, or an entry with an empty side or over 80 characters.
reuseIfIdenticalreuse_needs_sha256The upload was created without sha256, so the content cannot be recognised.
expectedBytesexpected_bytes_invalidNot a positive integer within the 2 GiB limit.
sha256sha256_invalidNot 64 hexadecimal characters.
since / untildate_invalidNot a valid ISO 8601 date-time.
cursorcursor_invalidThe cursor did not come from a nextCursor of this API.
metadatametadata_invalidOver 20 pairs, or a key/value over its length limit.
externalIdexternal_id_invalidEmpty, or over 128 characters.
retentionDaysretention_invalidOutside the 1 to 365 day range.
waitwait_invalidOutside the 1 to 60 second range.

Transcript failures

When a transcript ends in status: "failed", its error field carries { code, message } with the reason. Failed transcripts are never charged.

error.codeMeaning
audio_too_longAudio longer than the 10 hour limit.
insufficient_balanceThe audio is longer than the remaining balance covers. Top up and resend.
audio_unreachableThe url could not be downloaded.
audio_invalidThe audio could not be decoded.
audio_too_largeThe downloaded file is over 2 GiB.
audio_mismatchThe stored audio does not match the sha256 declared on the upload.
engine_failedFailed on every retry. Nothing was charged; send it again.

status: "canceled" is not a failure: it is a transcript you stopped with POST /v1/transcripts/{id}/cancel. It is terminal, error stays null, and nothing is charged.

On this page