Integração

Como transcrever áudio por API, do zero ao texto

Três requisições HTTP transformam um arquivo de áudio em texto com timestamps. Sem SDK, sem fila para configurar, sem servidor de GPU.

Transcrever áudio por API é mais simples do que a maioria das integrações faz parecer. São três requisições: uma para criar a conta e pegar a chave, uma para mandar o áudio e uma para buscar o texto. Nenhum SDK, nenhuma dependência nova no seu projeto, nenhum servidor com GPU para manter.

Este post mostra o caminho inteiro em curl, e depois os três detalhes que costumam morder quem integra: o áudio que não está numa URL pública, a espera pelo resultado e o custo.

Diagrama dos três passos: POST para criar a transcrição, resposta imediata com status processing, e o texto pronto com status done. No rodapé, as duas formas de saber que terminou: consultar o endpoint ou receber um webhook.
Os três passos. O que muda entre uma integração simples e uma boa é só o último: consultar em laço ou receber um webhook.

1. A chave

Crie a conta e copie a chave que nasce com ela. Não pede cartão, e a conta já vem com US$ 2 em créditos, o suficiente para cerca de 65 horas de áudio. A chave vai no header Authorization em toda requisição:

export TRANSCREVO_API_KEY="sua-chave"

2. Mandar o áudio

Se o arquivo já está numa URL pública (um bucket, um CDN, o enclosure de um podcast), uma requisição basta:

curl https://api.transcrevo.com/v1/transcripts \
  -H "Authorization: Bearer $TRANSCREVO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/podcast.mp3" }'

A resposta volta na hora, antes de o áudio ser processado:

{
	"data": {
		"id": "6b9f2b81-1c9a-4f4e-9d5f-8f2a7c1e3b90",
		"status": "processing",
		"text": null,
		"language": null,
		"cost": 0,
		"model": "trv-1",
		"createdAt": "2026-09-01T18:00:00.000Z"
	}
}

Guarde o id. É por ele que você busca o resultado.

O áudio que não está numa URL pública

Arquivo local, ou num bucket privado, vai por upload. A API aceita o arquivo inteiro numa requisição, e aceita em pedaços quando ele é grande ou a rede é ruim: o envio em chunks retoma de onde parou em vez de recomeçar os 800 MB. Os detalhes dos dois caminhos estão em envio de arquivos. O resultado é o mesmo uploadId, que entra no lugar da url:

curl https://api.transcrevo.com/v1/transcripts \
  -H "Authorization: Bearer $TRANSCREVO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "uploadId": "upl_9f2b81c9a4f4e9d5" }'

3. Buscar o texto

O processamento é assíncrono. Consulte o id até o status virar done:

curl https://api.transcrevo.com/v1/transcripts/6b9f2b81-1c9a-4f4e-9d5f-8f2a7c1e3b90 \
  -H "Authorization: Bearer $TRANSCREVO_API_KEY"
{
	"data": {
		"id": "6b9f2b81-1c9a-4f4e-9d5f-8f2a7c1e3b90",
		"status": "done",
		"text": "Bom dia a todos. Vamos começar.",
		"words": [
			{ "start": 0.32, "end": 0.78, "text": "Bom", "speaker": null },
			{ "start": 0.78, "end": 1.04, "text": "dia", "speaker": null }
		],
		"language": "pt",
		"durationSeconds": 3,
		"cost": 0.0025,
		"currency": "USD"
	}
}

text é a transcrição corrida. words é o que serve para legenda, para busca por trecho e para cortar o vídeo no ponto exato da palavra: cada palavra volta com start e end em segundos decimais.

Não fique consultando em laço

Polling funciona para um arquivo. Para volume, ele vira custo de requisição e atraso: você descobre que terminou no próximo tique, não quando terminou. Passe webhookUrl na criação e a API chama você uma vez, quando concluir:

curl https://api.transcrevo.com/v1/transcripts \
  -H "Authorization: Bearer $TRANSCREVO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/podcast.mp3",
    "webhookUrl": "https://seu-servidor.com/hooks/transcrevo"
  }'

O corpo do webhook traz o id e o status, mas não traz text nem words: num áudio de duas horas esses campos passam de 10 MB, e webhook não é lugar de trafegar isso. Recebeu a chamada, busque o transcrito pelo id.

Os parâmetros que mudam o resultado

Quatro campos opcionais respondem por quase toda a diferença entre uma integração que funciona e uma que decepciona:

CampoQuando usar
languageQuando você já sabe o idioma. Em áudio curto ou com ruído, a detecção automática erra mais do que você informando.
speakersQuando precisa saber quem falou o quê. Muda a tarifa, e não melhora o texto.
glossarySempre que o áudio tiver nome próprio, marca ou jargão do seu domínio. É o maior ganho de qualidade por linha de código.
webhookUrlSempre que houver volume.

O glossary merece atenção. Nome próprio e anglicismo são o erro mais comum de qualquer sistema de transcrição, porque são exatamente as palavras que não aparecem no treino. Você passa o que ele ouve e o que deveria escrever:

{
	"url": "https://example.com/reuniao.mp3",
	"glossary": { "gatu": "Gatto", "bertual": "Bertuol" }
}

O que custa

US$ 0,03 por hora de áudio, cobrada proporcional ao segundo: dois minutos custam US$ 0,001, não um centavo. Com speakers, US$ 0,05 por hora. O débito acontece quando a transcrição fica done, e enquanto processa o cost é 0. Os detalhes, incluindo o desconto automático acima de 10.000 horas, estão em preços e limites.

O que dá errado

  • 402 insufficient_balance: acabou o crédito. Nada é apagado, e a recarga automática existe para isso não acontecer no meio da noite.
  • audio_too_long: o limite é 10 horas por arquivo. Divida antes de enviar.
  • 429 rate_limited: você passou de 120 criações por minuto. Espere e tente de novo.
  • 429 concurrency_limit: enquanto a conta roda só com o crédito de boas-vindas, no máximo duas transcrições ficam em processamento ao mesmo tempo. A primeira recarga tira o teto.

A lista completa de códigos está em erros.

Resumo

Uma chave, um POST com a url ou o uploadId, e um GET (ou um webhook) para pegar o texto. O resto é opcional e existe para casos específicos: speakers para saber quem falou, glossary para acertar os nomes, language para não depender da detecção automática.

Todos os posts