Webhooks vs Polling para Transcrições: A Escolha Assíncrona
apiwebhooksdesenvolvedores

Webhooks vs Polling para Transcrições: A Escolha Assíncrona

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

Summarize this article with:

TL;DR

Use polling quando você tiver menos de 50 jobs por dia ou um usuário estiver acompanhando uma barra de progresso ativamente. Migre para webhooks quando o volume passar de cerca de 100 jobs por dia, quando os jobs rodarem em segundo plano, ou quando os resultados precisarem ser distribuídos para vários destinos. A conta é clara: com 200 jobs por dia, os webhooks reduzem as chamadas de API em 18x. A parte delicada é implementá-los com segurança: verificação HMAC, idempotência e saber quais fornecedores realmente oferecem um mecanismo de callback.

Use polling quando você tiver menos de 50 jobs por dia ou um usuário estiver aguardando ativamente. Migre para webhooks acima de aproximadamente 100 jobs por dia, quando os jobs rodarem em segundo plano. Essa é a decisão em duas frases. Tudo abaixo são os detalhes de implementação que fazem a diferença entre um webhook que funciona e um que perde transcrições silenciosamente às 2 da manhã.

A Matemática dos Recursos

Um exemplo concreto. Você transcreve 200 jobs por dia. Cada um leva 3 minutos em média.

Polling a cada 5 segundos:

  • Cada job: 36 requisições de polling em média (3 min x 12 polls/min)
  • Por dia: 7.200 requisições de polling
  • Resultados úteis nessas requisições: cerca de 1%

Webhooks:

  • Cada job: 1 envio + 1 entrega de webhook
  • Por dia: 200 envios + 200 webhooks = 400 eventos
  • 100% dos eventos trazem um resultado

Os webhooks são 18x mais eficientes nesse volume. Com 2.000 jobs por dia, o polling se torna genuinamente caro dos dois lados da API, e a maioria dos provedores começa a impor rate limit nas verificações de status antes de impô-lo nos envios.

Quando o Polling Ainda É a Escolha Certa

Você não tem um endpoint HTTP público. Ambientes locais de desenvolvimento, apps mobile-first sem backend e ferramentas internas atrás de uma VPN não conseguem receber entregas de webhook. O polling exige apenas HTTP de saída.

O volume é inferior a 50 jobs por dia. O custo de recursos é desprezível. A simplicidade de um loop de polling supera a infraestrutura de um receptor de webhook.

O usuário está acompanhando ativamente. Um usuário enviou uma entrevista e está de olhos fixos em uma barra de progresso. O polling te dá algo com que atualizar a interface. Webhooks exigiriam infraestrutura adicional (WebSocket, server-sent events ou um serviço de push) para levar o sinal de conclusão de volta ao navegador. Veja como construir com APIs de transcrição para um loop de polling funcional.

Quando Migrar para Webhooks

Três sinais:

O volume ultrapassa a marca de 100 jobs por dia. O custo de infraestrutura de um receptor de webhook se paga em economia de chamadas de API dentro de uma semana.

A latência não importa porque o usuário não está assistindo. Processamento em segundo plano de gravações que vão parar em um banco de dados para consulta posterior. O usuário verá o resultado na próxima vez que abrir o painel.

Os resultados precisam ser distribuídos para vários destinos. Um webhook pode disparar a gravação no seu banco de dados, uma notificação no Slack e uma atualização no HubSpot simultaneamente. O polling precisa fazer isso serialmente no código da sua aplicação. Veja integração de transcrição com Slack, Notion ou HubSpot para as receitas de fan-out.

Quais APIs de Transcrição Realmente Suportam Webhooks

Isso varia mais do que a documentação sugere.

FornecedorMecanismo assíncronoParâmetro de entregaVerificação de assinaturaRetentativas
AssemblyAIWebhook (POST)webhook_url no corpoCabeçalho personalizado (você configura nome + valor)10 tentativas, 10s entre cada
DeepgramCallback (POST)parâmetro de query callbackcabeçalho dg-token (identificador da chave de API)10 tentativas, 30s entre cada
Rev.aiWebhook (POST)objeto notification_configCabeçalho de autenticação via configA cada 30 min por até 24 h
AWS TranscribeSomente pollingGetTranscriptionJobN/AN/A
Google Cloud STTSomente polling (operação de longa duração)Operations.getN/AN/A
OpenAI WhisperSomente síncronoN/AN/AN/A

O AWS Transcribe não tem entrega via webhook nenhuma. Você envia um job e depois faz polling em GetTranscriptionJob até que TranscriptionJobStatus seja igual a COMPLETED. O LongRunningRecognize do Google Cloud Speech-to-Text retorna um handle de operação que você consulta com Operations.get até que done seja verdadeiro. O endpoint /v1/audio/transcriptions da OpenAI é totalmente síncrono: você fica bloqueado até a resposta voltar ou até estourar o tempo limite.

Minha opinião: o mecanismo de callback do Deepgram é o mais amigável para produção entre os três que suportam entrega assíncrona. O payload da AssemblyAI entrega apenas um transcript_id e um campo status na conclusão, o que significa que você sempre precisa de uma segunda chamada de API para buscar a transcrição em si. O Deepgram envia o resultado completo para a sua URL de callback.

Para uma análise mais profunda de como as principais APIs precificam seu uso, veja preços de APIs de fala para texto em 2026.

Configurando um Webhook (API do ConvertAudioToText)

A chamada de registro:

curl -X POST https://api.convertaudiototext.com/api/v1/dashboard/webhooks \
  -H "Authorization: Bearer $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. A API gera o secret no lado do servidor e o retorna uma única vez, no momento da criação. Ele não é armazenado de forma recuperável.

Eventos disponíveis: job.queued, job.processing, job.completed, job.failed. A maioria das integrações assina apenas job.completed e job.failed.

Ferramenta de upload de áudio do ConvertAudioToText - jobs enviados aqui disparam entregas de webhook na conclusão
Ferramenta de upload de áudio do ConvertAudioToText - jobs enviados aqui disparam entregas de webhook na conclusão

Validação de Assinatura HMAC

Cada entrega inclui um cabeçalho X-Webhook-Signature com o formato de valor sha256=<hex>. A assinatura é HMAC-SHA256(secret, raw_body).

Em Node:

import crypto from 'crypto';
import express from 'express';

const app = express();

app.post('/webhooks/transcription',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const sig = req.header('X-Webhook-Signature');
    const expected = 'sha256=' + crypto
      .createHmac('sha256', process.env.WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
      return res.status(401).send('Invalid signature');
    }

    const event = JSON.parse(req.body.toString());
    handleTranscript(event);
    res.sendStatus(200);
  }
);

Em Python:

import hmac
import hashlib

def verify(signature: str, body: bytes, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Use timingSafeEqual (Node) ou hmac.compare_digest (Python). Comparação ingênua de strings vaza informações pelo tempo de resposta.

Faça o parse do corpo após validar, nunca antes. Um middleware que faça o parse do JSON antes de o seu handler executar vai consumir os bytes brutos, tornando impossível calcular a assinatura. O exemplo com express.raw() acima é proposital: você recebe primeiro o buffer bruto e só então faz o parse.

Idempotência

O worker de entrega pode disparar mais de uma vez para o mesmo evento. Redes falham. Seu handler pode processar um evento e cair antes de retornar 200. O fornecedor tenta de novo. Seu handler roda outra vez.

Cada evento tem um event_id único (disponível no payload). O padrão seguro:

async function handleTranscript(event) {
  const inserted = await db.query(
    `INSERT INTO processed_events (event_id) VALUES ($1)
     ON CONFLICT DO NOTHING RETURNING id`,
    [event.data.job_id + ':' + event.event]
  );

  if (inserted.rowCount === 0) return;

  await saveTranscript(event.data.job_id, event.data.result_url);
}

ON CONFLICT DO NOTHING significa que entregas duplicadas saem silenciosamente. A primeira entrega vence; as duplicatas são ignoradas sem gerar erros.

Comportamento de Retentativas

O worker de entrega do ConvertAudioToText repete entregas que falharam neste cronograma (5 tentativas no total):

TentativaIntervalo
1Imediato
21 minuto
35 minutos
415 minutos
51 hora
66 horas

Após 5 tentativas malsucedidas, a entrega é marcada como failed. Você pode visualizar o histórico de entregas e disparar retentativas manuais pelo dashboard de webhooks, ou usar o endpoint de retry:

curl -X POST https://api.convertaudiototext.com/api/v1/dashboard/webhooks/deliveries/{delivery_id}/retry \
  -H "Authorization: Bearer $API_KEY"

Para que o seu handler se comporte corretamente: retorne 2xx para sucesso, 4xx para falhas permanentes (retry desnecessário), 5xx para falhas transitórias (retry desejado). Responda em até 30 segundos. Se o seu processamento for lento, confirme o recebimento imediatamente e coloque o trabalho real na fila:

app.post('/webhooks/transcription', (req, res) => {
  validateSignature(req);
  jobQueue.push(req.body);
  res.sendStatus(200); // ack first, process after
});

Desenvolvimento Local

Webhooks exigem uma URL HTTPS pública. Duas abordagens que funcionam:

Túnel ngrok ou cloudflared. Expõe o seu servidor local à internet com uma URL temporária.

ngrok http 3000
# Use the resulting https URL as your webhook URL

Handler em modo duplo. Webhooks em produção, polling no ambiente local de desenvolvimento. Uma variável de ambiente alterna o comportamento. Isso mantém o loop de feedback local simples sem alterar o código de produção.

Padrões de Produção

Direto ao banco de dados. O handler grava a transcrição no Postgres ou MongoDB e retorna 200. O código downstream consulta o banco de dados. O padrão mais simples, funciona em qualquer escala.

Integração direta. O handler publica no Slack, cria uma página no Notion ou atualiza um contato no HubSpot. Bom quando a integração é o destino principal. Falha de forma ruidosa (5xx) se o serviço downstream estiver fora do ar, o que aciona retentativas automaticamente.

Event bus. O handler publica em Kafka, SNS ou um tópico interno de pub/sub. Vários consumidores processam o mesmo evento de forma independente. Desacopla a recepção do webhook do processamento downstream. O padrão certo para pipelines grandes em que múltiplos sistemas consomem a mesma transcrição. Veja transcrição em lote para projetos grandes para a arquitetura do pipeline.

Combinando os Dois Padrões

Às vezes você quer os dois. O usuário envia um arquivo. Seu backend submete o job e começa a fazer polling para atualizações da interface. O webhook dispara ao concluir e se torna o registro canônico no seu banco de dados. O usuário recebe feedback imediato; o backend recebe um sinal assíncrono confiável que não depende da interface permanecer aberta.

Essa abordagem dupla exige mais código, mas entrega a melhor experiência de usuário quando os usuários aguardam ativamente resultados de arquivos com os quais se importam, enquanto o sistema também lida com jobs em segundo plano de forma confiável.

Se você só precisa de transcrições limpas sem construir uma integração de backend, o ConvertAudioToText cuida de todo o pipeline no navegador, sem necessidade de chave de API.

FAQ

Quando devo usar polling em vez de webhooks para transcrição?

Use polling quando você não tem um endpoint HTTP público (ambiente local de desenvolvimento, ferramentas acessíveis apenas via VPN, apps móveis sem backend), quando o volume é inferior a 50 jobs por dia, ou quando um usuário está aguardando ativamente e você precisa atualizar uma barra de progresso em tempo real. Webhooks adicionam custo de infraestrutura que o polling não justifica em volumes baixos.

Quais das principais APIs de transcrição suportam webhooks?

A AssemblyAI suporta webhooks por meio de um parâmetro webhook_url (10 tentativas de retry, timeout de 10 segundos). O Deepgram suporta um parâmetro de query callback com verificação pelo cabeçalho dg-token e 10 retentativas a cada 30 segundos. A Rev.ai usa um objeto notification_config com retentativas por até 24 horas. AWS Transcribe e Google Cloud Speech-to-Text exigem polling: AWS via GetTranscriptionJob e Google via a interface de operação de longa duração Operations.get. O OpenAI Whisper é totalmente síncrono, sem entrega assíncrona alguma.

Como validar que um webhook veio da fonte correta?

O padrão convencional é HMAC-SHA256: o provedor assina o corpo bruto da requisição com o seu segredo compartilhado e coloca o resultado em um cabeçalho. Seu handler recalcula o mesmo HMAC e compara usando uma função de igualdade segura contra ataques de timing. Nunca use uma comparação simples de strings, que vaza informações por meio do timing. O Deepgram usa um mecanismo diferente (um cabeçalho dg-token vinculado à sua chave de API), então a abordagem varia conforme o fornecedor.

O que meu handler de webhook deve retornar e com qual rapidez?

Retorne um código de status 2xx para confirmar o recebimento, e faça isso dentro da janela de tempo limite do seu fornecedor (30 segundos é o comum). Se o seu processamento real for caro, confirme imediatamente e coloque o trabalho na fila. Retorne 5xx para falhas transitórias que você quer ver repetidas, 4xx para falhas permanentes que não. Respostas lentas disparam tempestades de retentativas que podem congestionar sua fila de entrega.

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