Construindo com uma API de Transcrição: Primeira Integração
apidesenvolvedorestutorial

Construindo com uma API de Transcrição: Primeira Integração

BMMamane B. MoussaMay 26, 2026Updated July 2, 202611 min read

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" }.

Ferramenta de upload de áudio do ConvertAudioToText
Ferramenta de upload de áudio do ConvertAudioToText

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:

CamadaLimiteEscopo
Global100 requisições/minPor endereço IP
Tarefas de transcrição10 envios/minPor usuário autenticado
Upload multipart (chamadas assinadas)1000/minPor 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

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