
Construindo com uma API de Transcrição: Primeira Integração
Summarize this article with:
A Integração em Cinco Passos
Você pode adicionar transcrição a qualquer backend em menos de uma hora: obtenha uma chave de API, envie o áudio via POST (por upload de arquivo ou URL), faça polling ou receba um webhook na conclusão, busque o resultado e exporte no formato que precisar. Os padrões aqui são demonstrados com a API do ConvertAudioToText, mas o formato de enviar-e-depois-consultar é padrão em todos os provedores de transcrição assíncrona. Se você ainda está escolhendo um provedor, veja primeiro a comparação de APIs de transcrição 2026.
Os cinco passos são: autenticar, enviar uma tarefa, obter o resultado, tratar erros e exportar. Cada seção abaixo cobre um passo com código executável.
Passo 1: Obtenha uma Chave de API
O acesso à API no ConvertAudioToText exige o plano Business. Dentro do painel, navegue até Configurações, depois Chaves de API e crie uma nova chave. Dê a ela um nome descritivo ligado à integração ("production-backend", "staging-ingest"). A chave é exibida apenas uma vez, então copie-a imediatamente.
Formato da chave: ck_live_ seguido de 64 caracteres hexadecimais. Trate-a como uma senha. Armazene-a em uma variável de ambiente, nunca no controle de versão.
Toda requisição usa Authorization: Bearer <key>:
curl https://api.convertaudiototext.com/api/v1/result/some-job-id \
-H "Authorization: Bearer ck_live_..."
Em Node.js:
const headers = { 'Authorization': `Bearer ${process.env.CATT_API_KEY}` };
Em Python:
headers = {'Authorization': f'Bearer {os.environ["CATT_API_KEY"]}'}
Passo 2: Envie uma Tarefa de Transcrição
O endpoint é POST /api/v1/transcribe. Ele retorna HTTP 201 imediatamente com um job_id; a transcrição em si é assíncrona.
Existem dois caminhos de envio: upload de arquivo e URL. Ambos retornam o mesmo formato de resposta.
Upload de Arquivo
Use este caminho quando o áudio estiver local ou quando você o receber como upload multipart dos seus usuários.
import FormData from 'form-data';
import { createReadStream } from 'fs';
import fetch from 'node-fetch';
async function submitFileJob(filePath, language = 'en') {
const form = new FormData();
form.append('source', 'upload');
form.append('language', language);
form.append('file', createReadStream(filePath));
const res = await fetch('https://api.convertaudiototext.com/api/v1/transcribe', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.CATT_API_KEY}` },
body: form
});
if (!res.ok) throw new Error(`Submit failed: ${res.status}`);
const { job_id, status } = await res.json();
return { job_id, status };
}
Envio por URL
Use este caminho quando o áudio já estiver em armazenamento em nuvem (S3, R2, GCS ou qualquer URL HTTPS pública). O servidor baixa em alta largura de banda direto da origem, o que é mais rápido do que reenviar da sua máquina.
async function submitURLJob(audioURL, language = 'en') {
const res = await fetch('https://api.convertaudiototext.com/api/v1/transcribe', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CATT_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
source: 'url',
input_url: audioURL,
language: language
})
});
if (!res.ok) throw new Error(`Submit failed: ${res.status}`);
const { job_id, status } = await res.json();
return { job_id, status };
}
Ambos retornam { job_id: "uuid-string", status: "queued" }.

A ferramenta de upload de áudio que a API alimenta. O mesmo pipeline de tarefas roda tanto na interface web quanto na API.
Passo 3: Obtenha o Resultado
Uma tarefa concluída fica disponível em GET /api/v1/result/{job_id}. Dois padrões para aguardar: polling e webhooks.
Polling
Faça polling a cada 5 segundos até que status seja "completed" ou "failed". O endpoint responde instantaneamente; o atraso fica inteiramente do lado da transcrição.
async function pollForResult(jobId, maxWaitMs = 600000) {
const start = Date.now();
while (Date.now() - start < maxWaitMs) {
const res = await fetch(
`https://api.convertaudiototext.com/api/v1/result/${jobId}`,
{ headers: { 'Authorization': `Bearer ${process.env.CATT_API_KEY}` } }
);
const data = await res.json();
if (data.status === 'completed') return data;
if (data.status === 'failed') throw new Error(data.error || 'Job failed');
await new Promise(r => setTimeout(r, 5000));
}
throw new Error('Timeout waiting for transcription result');
}
Não faça polling mais rápido do que uma vez por segundo por tarefa. O limite global é de 100 requisições por minuto por IP, então um loop apertado entre muitas tarefas vai dispará-lo.
Webhooks
Para melhor uso de recursos em escala, webhooks substituem completamente o polling. Registre uma URL de webhook uma única vez:
curl -X POST https://api.convertaudiototext.com/api/v1/dashboard/webhooks \
-H "Authorization: Bearer $CATT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/transcription",
"events": ["job.completed", "job.failed"]
}'
A resposta inclui um campo secret. Guarde-o. O servidor fará POST na sua URL a cada evento correspondente. O payload tem esta aparência:
{
"event": "job.completed",
"timestamp": "2026-07-01T14:22:05Z",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"duration": 1842,
"result_url": "https://api.convertaudiototext.com/api/v1/result/550e8400-...",
"completed_at": "2026-07-01T14:22:04Z"
}
}
Verifique cada entrega com HMAC-SHA256. O servidor envia a assinatura em X-Webhook-Signature como sha256=<hex>:
import crypto from 'crypto';
function verifyWebhookSignature(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// In your Express/Fastify handler:
app.post('/webhooks/transcription', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-webhook-signature'];
if (!verifyWebhookSignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const { event, data } = JSON.parse(req.body);
if (event === 'job.completed') {
// fetch full transcript from data.result_url
}
res.status(200).end();
});
Webhooks exigem o plano Business e uma URL HTTPS publicamente acessível. Para uma comparação mais profunda das duas abordagens, veja o guia de webhook vs polling para transcrições.
Passo 4: Interprete o Resultado
A resposta de uma tarefa concluída aninha a transcrição sob a chave transcript. O formato exato é controlado pelo formatador do lado do servidor, mas os campos que você sempre encontrará são:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"language": "en",
"duration": 1842,
"filename": "interview.mp3",
"transcript": {
"text": "Hello, welcome to the show...",
"summary": "An interview about...",
"confidence": 0.97,
"word_count": 4210,
"timeline": [
{
"speaker": "Speaker 0",
"text": "Hello, welcome to the show.",
"start": 0.12,
"end": 1.87
}
],
"topics": [...],
"sentiments": [...]
}
}
O campo text é a transcrição completa concatenada. O array timeline traz falas atribuídas a cada falante com marcações de tempo. Os campos summary, topics e sentiments são preenchidos para áudio em inglês nos planos que incluem insights de IA; eles serão removidos ou estarão ausentes no plano Starter.
Passo 5: Formatos de Exportação
Baixe arquivos formatados via:
curl "https://api.convertaudiototext.com/api/v1/result/$JOB_ID/srt" \
-H "Authorization: Bearer $CATT_API_KEY" > captions.srt # SRT for video captions
curl "https://api.convertaudiototext.com/api/v1/result/$JOB_ID/vtt" \
-H "Authorization: Bearer $CATT_API_KEY" > captions.vtt # WebVTT
curl "https://api.convertaudiototext.com/api/v1/result/$JOB_ID/txt" \
-H "Authorization: Bearer $CATT_API_KEY" > transcript.txt # Plain text with speaker labels
curl "https://api.convertaudiototext.com/api/v1/result/$JOB_ID/json" \
-H "Authorization: Bearer $CATT_API_KEY" > transcript.json # Structured JSON
curl "https://api.convertaudiototext.com/api/v1/result/$JOB_ID/docx" \
-H "Authorization: Bearer $CATT_API_KEY" > transcript.docx # DOCX
Todos os endpoints de exportação exigem um plano pago. Requisições do plano gratuito retornam HTTP 402 com um campo upgrade_url. Para construir fluxos de legendas em escala, os endpoints SRT e VTT funcionam bem em pipelines em lote.
Tratamento de Erros
Três categorias de erros para tratar, cada uma com uma estratégia de recuperação diferente.
Erros 4xx são culpa sua. Autenticação inválida (401), campos obrigatórios ausentes (400), cota esgotada (402) ou limite de requisições atingido (429). Não repita a chamada sem corrigir a requisição. Em caso de 429, leia o campo retry_after do corpo da resposta.
Erros 5xx são transitórios. Repita com backoff exponencial. A maioria se resolve em até 30 segundos.
Falhas no nível da tarefa. Uma tarefa pode ser enviada com sucesso (HTTP 201) mas falhar depois durante o processamento. O status passa a ser "failed" e o error traz o motivo. Causas comuns: áudio corrompido, gravações inteiramente silenciosas, codecs não suportados. Repita a tarefa somente se a fonte de áudio for válida.
async function transcribeWithRetry(audioPath, maxAttempts = 3) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
const { job_id } = await submitFileJob(audioPath);
return await pollForResult(job_id);
} catch (err) {
// 4xx: fix the request, do not retry
if (err.status >= 400 && err.status < 500) throw err;
// Last attempt: give up
if (attempt === maxAttempts - 1) throw err;
// 5xx: wait and try again
const delayMs = 1000 * Math.pow(2, attempt);
await new Promise(r => setTimeout(r, delayMs));
}
}
}
Limites de Requisições
Os limites que se aplicam às requisições autenticadas da API:
| Camada | Limite | Escopo |
|---|---|---|
| Global | 100 requisições/min | Por endereço IP |
| Tarefas de transcrição | 10 envios/min | Por usuário autenticado |
| Upload multipart (chamadas assinadas) | 1000/min | Por IP, teto separado |
O limite de tarefas de transcrição significa que você pode enviar até 10 tarefas por minuto, não 10 simultâneas; tarefas além desse limite entram em fila no servidor.
Ao atingir qualquer um desses limites, a resposta é HTTP 429 com retry_after em segundos no corpo da resposta.
Um Exemplo Completo e Funcional
import { createReadStream } from 'fs';
import FormData from 'form-data';
import fetch from 'node-fetch';
const API_KEY = process.env.CATT_API_KEY;
const BASE = 'https://api.convertaudiototext.com/api/v1';
async function submit(filePath, language = 'en') {
const form = new FormData();
form.append('source', 'upload');
form.append('language', language);
form.append('file', createReadStream(filePath));
const res = await fetch(`${BASE}/transcribe`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` },
body: form
});
if (!res.ok) throw Object.assign(new Error('Submit failed'), { status: res.status });
return res.json();
}
async function poll(jobId, maxWaitMs = 600000) {
const deadline = Date.now() + maxWaitMs;
while (Date.now() < deadline) {
const res = await fetch(`${BASE}/result/${jobId}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const data = await res.json();
if (data.status === 'completed') return data;
if (data.status === 'failed') throw new Error(data.error || 'Transcription failed');
await new Promise(r => setTimeout(r, 5000));
}
throw new Error('Timeout');
}
export async function transcribeFile(filePath, language = 'en') {
const { job_id } = await submit(filePath, language);
const result = await poll(job_id);
return result.transcript?.text ?? '';
}
A partir daqui, adicione cache para resultados de transcrição para evitar reprocessar arquivos idênticos, e troque o polling por webhooks assim que tiver mais do que algumas poucas tarefas simultâneas.
Minha opinião: o formato enviar-consultar é o mesmo em todos os grandes provedores de transcrição assíncrona, então o código cliente pode ser transferido quase sem alterações. As partes que diferem são o formato do cabeçalho de autenticação, os nomes dos campos na resposta e se recursos avançados como rótulos de falantes exigem um plano superior. Verifique cada um deles na documentação atual do provedor antes de colocar em produção.
Se você precisa apenas de uma transcrição limpa ocasionalmente, sem gerenciar chaves de API ou níveis de plano, o ConvertAudioToText permite enviar, transcrever e copiar sua transcrição gratuitamente, sem configuração.
Perguntas Frequentes
Qual plano eu preciso para usar a API?
A criação de chave de API no ConvertAudioToText exige o plano Developer ou Business. O Developer inclui 600 minutos por mês; o Business é ilimitado. Consulte /pricing para conferir os planos mais recentes antes de comprar.
Qual é o limite de requisições para tarefas de transcrição?
O limite global é de 100 requisições por minuto por endereço IP. Os envios de tarefas de transcrição são ainda limitados a 10 tarefas por minuto por usuário. Ao atingir qualquer um desses limites, a resposta é HTTP 429 com o cabeçalho Retry-After.
Polling ou webhooks: qual devo usar?
O polling funciona para integrações de baixo volume ou pontuais e não exige endpoint público. Webhooks são melhores quando você tem várias tarefas simultâneas: em vez de consultar N tarefas a cada poucos segundos, seu servidor recebe um POST por conclusão. Webhooks exigem o plano Business e uma URL HTTPS publicamente acessível.
Como verifico se um payload de webhook é autêntico?
Cada registro de webhook retorna uma string secreta. Quando o servidor entrega um payload, ele calcula o HMAC-SHA256 do corpo JSON bruto usando esse segredo e envia o resultado no cabeçalho X-Webhook-Signature como sha256=hex. Recalcule o mesmo HMAC do seu lado e compare. Rejeite qualquer requisição em que eles não coincidirem.
Quais formatos de exportação a API suporta?
Tarefas concluídas podem ser exportadas como SRT, VTT, TXT, JSON, DOCX e PDF via GET /api/v1/result//. Todos os endpoints de exportação exigem um plano pago; requisições do plano gratuito retornam HTTP 402.
Fontes
- Código-fonte do backend do ConvertAudioToText:
/backend/internal/api/handlers/transcribe.go,webhook.go,apikey.go,middleware/ratelimit.go,services/apikey/service.go,services/webhook/service.go,services/billing/plans.go(lido diretamente do repositório, 2026-07-02) - Suporte a idiomas da Deepgram: https://developers.deepgram.com/docs/models-languages-overview
- Preços e níveis de plano do ConvertAudioToText: https://convertaudiototext.com/pricing
Try transcription free
Convert any audio or video to clean, unwatermarked text — speaker labels, timestamps, and AI summaries included. First 10 minutes free, no account.
Related Articles

Speechmatics Alternative for Non-Developers: Web Transcription Without Code
Speechmatics is genuinely excellent for developers: 50 hours free per month, 56 languages, on-prem deployment. If you need a drag-and-drop web app with flat $9.99/mo pricing instead of an API, here is an honest comparison of the two.

Best Transcription Tools with API Access (2026)
Which transcription SaaS tools actually give you API keys, and on which plan? Verified pricing and plan gates for Descript, Sonix, Fireflies, Happy Scribe, AssemblyAI, and more.