Erros
O envelope de erro da API e a tabela completa de códigos, de validation e invalid_api_key a insufficient_balance, rate_limited e concurrency_limit.
Erros seguem o envelope padrão:
{
"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" }
}
}messageexplica em texto o que houve. É o campo para ler durante a integração e para registrar no seu log.codeidentifica o tipo do erro e é o que o seu código deve testar: sempre um dos códigos abaixo, nunca uma string arbitrária.retryablediz se repetir a MESMA requisição pode dar outro resultado. É a única decisão que o código HTTP não responde sozinho: um429se repete, um409de idempotência nunca vai passar. Ramifique por ele em vez de manter a sua própria tabela de quais erros valem retry.requestIdé o identificador desta resposta, também no headerX-Request-Id. Guarde-o no seu log: é por ele que encontramos, do nosso lado, o que aconteceu naquela chamada específica.fields(apenas emvalidation) aponta uma chave estável por campo inválido.
retryable: true não quer dizer "repita agora": em rate_limited o
Retry-After diz quando, e nos demais vale espera exponencial com jitter.
Códigos
| HTTP | code | Quando acontece |
|---|---|---|
| 400 | validation | Corpo inválido. Veja fields. |
| 400 | chunk_empty | Chunk sem conteúdo. |
| 400 | upload_mismatch | O recebido não bate com o expectedBytes declarado no upload. |
| 401 | invalid_api_key | Chave ausente ou inválida no header Authorization. |
| 402 | insufficient_balance | Sem créditos. Recarregue no painel. |
| 404 | not_found | Transcrição inexistente ou de outra conta. |
| 404 | upload_not_found | Upload inexistente ou de outra conta. |
| 409 | upload_busy | Outro chunk do mesmo upload ainda está sendo gravado. |
| 409 | idempotency_conflict | Idempotency-Key reaproveitada com outro corpo. |
| 409 | transcript_processing | Transcrição ainda processando não pode ser apagada. |
| 409 | transcript_finished | A transcrição já terminou e não pode mais ser cancelada. |
| 409 | no_webhook | A transcrição não tem webhookUrl (ou não terminou) para reenviar. |
| 413 | chunk_too_large | Chunk acima de 16 MiB. |
| 413 | upload_too_large | Arquivo (ou upload) passaria de 2 GiB. |
| 429 | rate_limited | Limite de requisições atingido. Veja o header Retry-After. |
| 429 | concurrency_limit | Conta ainda sem recarga com 2 transcrições já rodando. |
| 500 | internal | Erro nosso. Tente de novo; se persistir, fale com o suporte. |
A referência da API documenta, por endpoint, exatamente quais códigos cada status pode devolver.
Limites de requisição
Toda resposta de uma rota limitada traz os três cabeçalhos, não só o 429:
| Cabeçalho | O que diz |
|---|---|
RateLimit-Limit | O teto da janela |
RateLimit-Remaining | Quantas requisições ainda cabem nela |
RateLimit-Reset | Em quantos segundos a próxima vaga abre |
É com eles que dá para desacelerar ANTES de bater no teto, em vez de descobri-lo
sendo recusado. O 429 rate_limited traz ainda Retry-After com a espera real,
em segundos. Os tetos são por conta, em janela deslizante:
| Caminho | Teto por minuto |
|---|---|
POST /v1/transcripts | 120 |
POST /v1/uploads | 120 |
PUT /v1/uploads/{id} | 600 |
Um backlog destravando de uma vez encosta nesses tetos com facilidade, porque o
pico de um cliente não se parece com o tráfego médio dele. Espalhe a rajada pela
espera do Retry-After em vez de tentar de novo na hora; nada é perdido, só
adiado. Precisando de mais, fale com a gente antes de a migração começar.
Chaves de validação
| Campo | Chave | Significado |
|---|---|---|
url | url_invalid | Não é uma URL http/https válida. |
url | url_or_upload_id | Informe url ou uploadId, exatamente um. |
uploadId | upload_id_invalid | Formato inválido. |
uploadId | upload_empty | O upload não recebeu nenhum byte. |
language | language_invalid | Código de idioma inválido. |
speakers | speakers_invalid | Não é true, um inteiro de 1 a 20 ou "min-max". |
idempotencyKey | idempotency_key_invalid | Passa de 64 caracteres. |
webhookUrl | webhook_url_invalid | Não é uma URL http/https pública alcançável. |
glossary | glossary_invalid | Passa de 100 entradas, ou alguma tem lado vazio ou acima de 80 caracteres. |
reuseIfIdentical | reuse_needs_sha256 | O upload foi criado sem sha256, então não há como reconhecer o conteúdo. |
expectedBytes | expected_bytes_invalid | Não é um inteiro positivo dentro do limite de 2 GiB. |
sha256 | sha256_invalid | Não são 64 caracteres hexadecimais. |
since / until | date_invalid | Não é uma data ISO 8601 válida. |
cursor | cursor_invalid | O cursor não veio de um nextCursor desta API. |
metadata | metadata_invalid | Passa de 20 pares, ou alguma chave/valor passa do tamanho. |
externalId | external_id_invalid | Vazio ou acima de 128 caracteres. |
retentionDays | retention_invalid | Fora do intervalo de 1 a 365 dias. |
wait | wait_invalid | Fora do intervalo de 1 a 60 segundos. |
Falhas de transcrição
Quando uma transcrição termina em status: "failed", o campo error dela traz
{ code, message } com o motivo. Transcrições que falham não são cobradas.
error.code | Significado |
|---|---|
audio_too_long | Áudio acima do limite de 10 horas. |
insufficient_balance | O áudio é mais longo do que o saldo restante cobre. Recarregue e reenvie. |
audio_unreachable | Não foi possível baixar a url. |
audio_invalid | O áudio não pôde ser decodificado. |
audio_too_large | O arquivo baixado passa de 2 GiB. |
audio_mismatch | O áudio guardado não bate com o sha256 declarado no upload. |
engine_failed | Falhou em todas as tentativas. Nada foi cobrado; reenvie. |
status: "canceled" não é falha: é uma transcrição que você cancelou com
POST /v1/transcripts/{id}/cancel. Ela é terminal, error fica null e nada é
cobrado.
Precisão e qualidade
WER de 6,34% em português e DER de 4,6% na separação de falantes, medidos por nós contra transcrição humana, com o protocolo de cada número.
Repetição segura
Idempotency-Key, o campo retryable, espera longa com ?wait=, cancelamento, cursor e os limites que a própria API informa: o que uma integração precisa para se defender sozinha.