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.
Uma integração de transcrição roda sem ninguém olhando: rede pisca, um deploy reinicia o processo no meio de uma chamada, um backlog destrava de uma vez. Esta página é o que a API oferece para nada disso virar áudio cobrado duas vezes ou job perdido.
Repetir a criação sem cobrar duas vezes
O POST /v1/transcripts cria E cobra. Uma resposta que se perde no caminho de
volta deixa o cliente sem saber se o job existe, e repetir às cegas cria o
segundo. O Idempotency-Key fecha essa janela:
curl https://api.transcrevo.com/v1/transcripts \
-H "Authorization: Bearer $TRANSCREVO_API_KEY" \
-H "Idempotency-Key: 8e1f0f1a-3c1a-4a1f-9f0a-2b7d5c9e1234" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/audio.mp3", "speakers": true}'A mesma chave devolve a mesma transcrição, sem criar nem cobrar outra. A
mesma chave com um corpo diferente é 409 idempotency_conflict: é erro do seu
lado, não repetição, e devolver o job antigo faria o áudio novo sumir sem aviso.
Escolha a chave pelo que é estável no seu lado. Com upload, chaveie pelo
PRIMEIRO uploadId: ele muda a cada nova tentativa de envio, então usar o id da
sua mídia daria 409 para sempre depois da primeira vez.
Saber o que vale repetir
Todo erro traz retryable:
{ "error": { "code": "rate_limited", "message": "…", "retryable": true, "requestId": "…" } }const { error } = await createTranscript(input);
if (error && !error.retryable) throw new Error(`${error.code}: ${error.message} (${error.requestId})`);
// retryable: espera exponencial, ou o Retry-After quando ele vierSem esse campo, cada integração mantém a própria tabela de "quais erros valem
retry", e a que erra repete para sempre o que jamais vai passar. retryable: true não quer dizer "repita agora": em rate_limited o Retry-After diz
quando.
Guarde o requestId (também no header X-Request-Id) no seu log. É por ele que
achamos, do nosso lado, o que houve naquela chamada.
Esperar o resultado sem ficar perguntando
GET /v1/transcripts/{id}?wait=30A resposta é segurada até a transcrição terminar, e volta no instante em que
ela termina. Uma requisição no lugar de um laço de polling. O teto é 60
segundos; a espera vencer não é erro (você recebe a transcrição ainda em
processing e chama de novo).
Enquanto está processing, a resposta traz Retry-After com o intervalo que
vale esperar antes da próxima pergunta.
Para muitos áudios ao mesmo tempo, webhookUrl continua sendo mais barato: uma
entrega por job, em vez de uma conexão aberta por job.
Cancelar
POST /v1/transcripts/{id}/cancelPara uma transcrição que ainda está processing. Ela vira canceled, terminal,
e nunca é cobrada; o crédito que ela segurava (reserved no
GET /v1/balance) é liberado na hora.
Cancelar é sobre a cobrança, não sobre a máquina: o áudio que já está numa GPU termina o trabalho em curso e o resultado é descartado. Nada dele chega até você nem à sua fatura.
Paginar um histórico longo
GET /v1/transcripts?cursor=<nextCursor>&perPage=100O cursor não pula nem repete transcrição quando novas chegam durante a
paginação, que é justamente o que o offset faz. Ele também não conta o total da
conta: total volta null por cursor, porque essa contagem é o custo que ele
existe para evitar.
?page= continua funcionando para quem já integrou com ele.
Perguntar os limites em vez de gravá-los no código
GET /v1/limitsDuração máxima por áudio, formatos aceitos, tetos de upload, de chunk e de corpo da requisição, glossário, e quantas transcrições esta conta pode rodar ao mesmo tempo. Ler no início do processo evita descobrir um teto depois de já ter subido o arquivo.
Rajada
Toda resposta de rota limitada traz RateLimit-Limit, RateLimit-Remaining e
RateLimit-Reset. Com eles a sua fila desacelera antes de bater no teto, em vez
de descobri-lo sendo recusada. Detalhes e códigos em Erros.