DocumentaçãoTranscrição ao vivo

Transcrição ao vivo

Envie o áudio do microfone ou de uma aba ao transcript1 por WebSocket e receba linhas de legenda, com tradução opcional em tempo real, enquanto as pessoas falam.

A transcrição ao vivo transforma um fluxo de áudio em linhas de legenda enquanto as pessoas ainda estão falando. Seu cliente envia áudio PCM bruto por um WebSocket, e o transcript1 devolve linhas que começam como um palpite parcial e depois se tornam finais. Adicione translate_to e cada linha também traz a tradução. Quando a sessão termina, o resultado é salvo como uma transcrição comum.

Não existem planos separados para o modo ao vivo: ele usa o mesmo saldo de minutos de todo o resto. Veja em Cobrança como o tempo conectado é contado.

Quando usar o modo ao vivo

Use o modo ao vivo quandoUse uma transcrição comum quando
O áudio ainda está sendo produzido: uma chamada, uma palestra, uma transmissão, um vídeo que você está assistindo.Você tem um link ou um arquivo pronto.
Você precisa de legendas em um ou dois segundos após a fala.Você quer a melhor precisão e pode esperar o resultado.
Você quer tradução em tempo real ao lado de cada linha.Você quer traduzir uma transcrição pronta, ou para vários idiomas.

O modo ao vivo recebe só áudio, como PCM bruto. Ele não aceita links de vídeo, uploads, WebM, Opus nem MP3. Capture o áudio no seu cliente e converta para PCM antes. Capturar áudio no navegador mostra como.

Início rápido com o SDK

Instale o SDK 1.5.0 ou mais recente: npm i @transcript1/sdk@latest. t1.live.start cria a sessão, conecta e é resolvido quando a sessão começa, então você já pode enviar áudio.

import { createReadStream } from 'node:fs';
import { setTimeout as sleep } from 'node:timers/promises';
import { Transcript1 } from '@transcript1/sdk';
 
const t1 = new Transcript1(); // reads TRANSCRIPT1_API_KEY
 
const live = await t1.live.start({ language: 'en', translateTo: 'es', sampleRate: 16000 });
 
live.on('line', line => {
  const mark = line.final ? 'final  ' : 'partial';
  console.log(mark, line.text, '→', line.translation?.text ?? '');
});
live.on('warning', warning => console.warn(warning.code));
 
// speech.pcm: 16 kHz, mono, signed 16-bit little-endian, no header
for await (const chunk of createReadStream('./speech.pcm', { highWaterMark: 3200 })) {
  live.send(chunk);
  await sleep(100); // 3,200 bytes are 100 ms of audio: send at real-time speed
}
 
const result = await live.end();
console.log(result.reason, result.transcriptionId, result.minutesCharged);

As linhas são atualizadas no mesmo lugar. Substitua uma linha que você já tem quando chegar outra com o mesmo id, ou leia todas em live.lines. end() encerra a sessão, espera as últimas linhas e salva a transcrição. Veja todos os métodos e eventos em SDK para Node.js.

Início rápido com um WebSocket puro

Sem o SDK são dois passos: criar uma sessão por HTTPS e abrir o WebSocket com o token de uso único que voltou.

Criar uma sessão

curl -X POST https://api.transcript1.com/v1/live/sessions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"language": "en", "translate_to": "es", "sample_rate": 16000}'

A API responde 201:

{
  "id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
  "status": "waiting",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "websocket_url": "wss://api.transcript1.com/v1/live/sessions/lv_8fK2Lm0PqRsTuVwXyZaBcD/stream",
  "token": "Zk3x...",
  "token_expires_at": "2026-11-02T12:01:00Z",
  "rate": 1
}

transcription_id é null quando save é false. O token é de uso único e expira em 60 segundos, então conecte-se logo. rate é o número de minutos de saldo cobrados por minuto iniciado desta sessão; veja Cobrança.

O corpo da requisição aceita estes campos. Todos são opcionais, e o escopo necessário é transcriptions:write:

CampoPadrãoRegra
languageautoauto, ou um código de duas letras que a transcrição ao vivo aceite.
translate_tonenhumUm código de duas letras. Um único idioma de destino por sessão. Veja Traduzir ao vivo.
diarizefalseIdentifica os falantes. Algumas configurações não conseguem, por exemplo junto com language: "auto", e respondem 422. Confira as capacidades.
audio_formatpcm_s16leO único valor. O campo existe para que outros formatos possam ser adicionados depois.
sample_rate16000Um inteiro de 8000 a 48000. Somente mono.
savetrueCom false, nada é mantido depois que a sessão termina. Veja Transcrições salvas.

Conectar e transmitir

Abra o WebSocket com o token como segundo subprotocolo. Isso funciona em navegadores e no Node 22+ sem cabeçalhos personalizados. Espere session.started antes de enviar áudio: os quadros binários enviados antes são descartados e você recebe um aviso audio_dropped.

import { createReadStream } from 'node:fs';
import { setTimeout as sleep } from 'node:timers/promises';
 
const session = await fetch('https://api.transcript1.com/v1/live/sessions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TRANSCRIPT1_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ language: 'en', translate_to: 'es', sample_rate: 16000 }),
}).then(response => response.json());
 
const ws = new WebSocket(session.websocket_url, ['t1-live', session.token]);
const lines = new Map();
 
ws.addEventListener('message', async message => {
  const event = JSON.parse(message.data);
 
  if (event.type === 'session.started') {
    for await (const chunk of createReadStream('./speech.pcm', { highWaterMark: 3200 })) {
      ws.send(chunk);
      await sleep(100);
    }
    ws.send(JSON.stringify({ type: 'end' }));
  } else if (event.type === 'line') {
    lines.set(event.line.id, event.line); // the full current state: replace by id
    console.log(event.line.final, event.line.text, event.line.translation?.text);
  } else if (event.type === 'session.ended') {
    console.log('ended:', event.reason, event.minutes_charged);
  }
});
 
ws.addEventListener('close', event => console.log('closed', event.code));

No Node 20 não há WebSocket nativo: use o pacote ws, que tem a mesma forma new WebSocket(url, protocols).

Para um token válido, o servidor responde com o subprotocolo t1-live e mais nada. Um token inválido nunca faz o handshake falhar: o socket abre, você recebe um evento error e o socket fecha com um código 4xxx, para que o navegador mostre o motivo real em vez de um 1006 genérico. Veja Códigos de fechamento.

Capturar áudio no navegador

O áudio ao vivo precisa ser PCM mono de 16 bits com sinal, little-endian, na sample_rate da sessão. Os navegadores entregam amostras em ponto flutuante, então converta-as em um AudioWorklet. Este trecho captura o microfone como PCM de 16 kHz em blocos de 100 ms e o envia com o ponto de entrada do SDK para navegador. getAccessToken devolve um token de acesso OAuth; nunca coloque uma chave de API em um navegador. Veja Segurança.

import { createLiveClient } from '@transcript1/sdk/live';
 
const workletSource = `
class Pcm16Processor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.buffer = new Int16Array(1600); // 100 ms at 16 kHz
    this.filled = 0;
  }
 
  process(inputs) {
    const channel = inputs[0][0];
    if (!channel) return true;
    for (let i = 0; i < channel.length; i++) {
      const sample = Math.max(-1, Math.min(1, channel[i]));
      this.buffer[this.filled++] = sample < 0 ? sample * 0x8000 : sample * 0x7fff;
      if (this.filled === this.buffer.length) {
        this.port.postMessage(this.buffer.slice());
        this.filled = 0;
      }
    }
    return true;
  }
}
registerProcessor('pcm16', Pcm16Processor);
`;
 
const client = createLiveClient({ getAccessToken: () => myApp.getOAuthAccessToken() });
 
const stream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: 1 } });
const context = new AudioContext({ sampleRate: 16000 });
const moduleUrl = URL.createObjectURL(new Blob([workletSource], { type: 'text/javascript' }));
await context.audioWorklet.addModule(moduleUrl);
 
// the context's real rate: a browser that cannot do 16 kHz picks its own
const live = await client.start({ language: 'auto', sampleRate: context.sampleRate });
live.on('line', renderLine);
 
const microphone = context.createMediaStreamSource(stream);
const capture = new AudioWorkletNode(context, 'pcm16');
capture.port.onmessage = event => live.send(event.data); // an Int16Array
const mute = context.createGain();
mute.gain.value = 0;
microphone.connect(capture).connect(mute).connect(context.destination);
 
async function stop() {
  stream.getTracks().forEach(track => track.stop());
  await context.close();
  return live.end();
}

O worklet foi escrito para 16 kHz. Se o contexto rodar em outra taxa, o bloco deixa de ter 100 ms, mas o PCM continua correto porque sampleRate coincide com o contexto. Para capturar o som de uma aba ou de um vídeo em vez do microfone, crie o MediaStream com getDisplayMedia ou a partir do captureStream() de um elemento de mídia e conecte-o da mesma forma.

Traduzir ao vivo

Adicione translate_to à sessão e o transcript1 traduz cada linha conforme ela é dita.

  • Um único idioma de destino por sessão. Depois, adicione mais idiomas à transcrição salva com o endpoint de traduções.
  • Cada line tem um original (text, final) e um objeto translation (language, text, final). translation é null quando translate_to não está definido.
  • O original e a tradução se tornam finais de forma independente: uma linha pode ter o original final enquanto a tradução ainda muda. Mostre uma linha como final quando os dois forem.
  • Uma linha que já está no idioma de destino usa o próprio texto como tradução e é final quando a linha fecha.
  • Uma linha fecha quando o falante faz uma pausa, quando o falante muda, quando uma pausa entre palavras passa de 0.9 segundo, quando a linha chega a duas fileiras de legenda (84 caracteres) ou a 15 segundos, ou quando você envia finalize. Com a tradução ativada, uma linha pode demorar cerca de 3 segundos a mais para fechar enquanto a tradução chega. Nesse caso a tradução pode ficar aberta (final: false) e continuar crescendo depois que o original já é final.
  • A tradução é gratuita. Ela não usa minutos.

Quais idiomas de destino e pares de idiomas estão disponíveis depende do provedor de voz ao vivo. Leia as capacidades em vez de fixar regras no código. Um destino não aceito retorna 422 unsupported_translation. Por exemplo, um provedor pode exigir um language explícito em vez de auto, ou permitir apenas pares que incluam inglês.

Protocolo WebSocket

Conexão

new WebSocket(websocket_url, ['t1-live', token]);

O token viaja como subprotocolo do WebSocket. Ele nunca aparece em uma URL nem em um log de acesso. Não envie o cabeçalho Authorization: o token é a credencial. O servidor envia um ping a cada 30 segundos; navegadores e o pacote ws respondem automaticamente. Um socket que não envia pong por 60 segundos é desconectado.

Uma conexão válida a uma sessão que já tem um socket substitui o anterior. Veja Retomada e substituição.

Mensagens do cliente

MensagemSignificado
Quadro binárioÁudio: PCM s16le mono na sample_rate da sessão. Cada quadro tem tamanho par e no máximo 256 KB. Cerca de 100 ms por quadro é um bom tamanho.
{"type":"finalize"}Fecha a linha atual agora, por exemplo quando o vídeo é pausado.
{"type":"end"}Encerra a sessão e a salva. O servidor responde session.ended e fecha o socket.
Qualquer outra coisaUm evento error não fatal com o código invalid_message.

Um quadro de tamanho ímpar ou grande demais é uma violação do protocolo e fecha o socket com 4008.

Comece a enviar áudio depois de session.started. Um cliente que envia áudio mais rápido que o tempo real paga pelo tempo do áudio em vez do tempo conectado (veja Cobrança), então envie na velocidade em que o áudio é produzido.

Eventos do servidor

Cada evento é um quadro de texto JSON com um type.

EventoEnviado quando
session.startedA conexão foi aceita. Envie áudio a partir de agora.
lineUma linha de legenda foi criada ou alterada.
usageDepois de cada cobrança.
warningAlgo pede atenção, mas a sessão continua.
errorUm problema não fatal, ou o motivo de um fechamento imediato.
reconnectO servidor está prestes a reiniciar. Retome a sessão.
session.endedA sessão terminou e foi salva. É o último evento antes de um fechamento normal.

session.started informa se é uma retomada e quanto áudio a sessão já processou:

{
  "type": "session.started",
  "session_id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "resumed": false,
  "offset_ms": 0
}

line traz o estado completo e atual de uma linha, então substitua a linha que você tem com o mesmo id. Veja O objeto de linha.

{
  "type": "line",
  "line": {
    "id": 12,
    "start_ms": 53120,
    "end_ms": 56040,
    "speaker": null,
    "language": "en",
    "text": "Welcome back to the stream",
    "final": true,
    "translation": { "language": "es", "text": "Bienvenidos de nuevo", "final": false }
  }
}

usage é enviado depois de cada cobrança, com o tempo conectado, o tempo de áudio, os minutos de saldo cobrados até agora e os minutos que restam:

{
  "type": "usage",
  "connected_ms": 61240,
  "audio_ms": 60100,
  "minutes_charged": 2,
  "minutes_remaining": 898
}

warning tem um code:

codeSignificado
balance_lowA próxima cobrança falharia. A sessão termina com balance_exhausted quando ela vencer. Não é enviado enquanto a recarga automática está ligada.
audio_droppedChegou áudio antes de a sessão estar pronta, ou áudio demais enquanto o provedor de voz se conectava. O áudio mais antigo foi descartado.
idle_timeout_soonSem áudio há 4 minutos. A sessão termina após 5 minutos sem áudio. Envie áudio para mantê-la.
{ "type": "warning", "code": "idle_timeout_soon" }

error tem um code e uma message. Estes códigos podem chegar em um socket ao vivo:

codeFatalSignificado
invalid_messageNãoUm quadro de texto que não é finalize nem end.
invalid_tokenSimO token é inválido, expirou ou já foi usado. O socket fecha com 4001.
not_foundSimA sessão não existe. O socket fecha com 4004.
session_endedSimA sessão já terminou. O socket fecha com 4004.
{ "type": "error", "code": "invalid_message", "message": "Expected {\"type\":\"finalize\"} or {\"type\":\"end\"}" }

reconnect chega antes de o servidor fechar o socket com 1012, por exemplo durante um deploy. Busque um token novo e retome.

{ "type": "reconnect", "reason": "server_restart" }

session.ended é o último evento de toda sessão. Os números são os totais finais:

{
  "type": "session.ended",
  "reason": "client_ended",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "minutes_charged": 3,
  "connected_ms": 142500,
  "audio_ms": 141900
}

O objeto de linha

CampoSignificado
idUm número que começa em 0 e cresce de um em um a cada linha. É o índice do segmento na transcrição salva.
start_ms, end_msInício e fim no tempo de áudio da sessão. Esse tempo continua após as reconexões.
speakerO rótulo do falante quando diarize está ativado; caso contrário, null. Os rótulos são os mesmos das transcrições comuns.
languageO idioma detectado ou informado da linha, ou null enquanto ainda não é conhecido.
textO texto original até agora.
finaltrue quando o texto original não vai mais mudar.
translationnull sem translate_to; caso contrário, language, text e final.

Mostre as linhas com final: false em um estilo mais claro: o texto delas ainda pode mudar. O texto de uma linha com final: true nunca mais muda, e uma linha final nunca é removida.

O SDK usa camelCase nos campos da linha: startMs, endMs.

Motivos de encerramento

session.ended e GET /v1/live/sessions/:id trazem um end_reason:

MotivoSignificado
client_endedVocê enviou end.
balance_exhaustedA cobrança seguinte falhou. A sessão termina na virada de um minuto e é salva.
idle_timeoutSem áudio por 5 minutos.
max_durationA sessão ficou conectada por 300 minutos. Inicie outra.
disconnectedO cliente ficou ausente por mais de 2 minutos sem retomar.
upstream_errorO provedor de voz falhou.
internal_errorTivemos um erro interno.
not_connectedA sessão nunca se conectou em 5 minutos. Nunca é cobrada e só aparece por GET.

Códigos de fechamento

CódigoSignificado
1000Fechamento normal. Sempre vem depois de session.ended.
1011Erro inesperado do servidor. O servidor recupera a sessão: retome com um token novo.
1012Reinício do servidor. Vem depois de reconnect: retome com um token novo.
4001O token é inválido, expirou ou já foi usado.
4004A sessão não foi encontrada ou já terminou.
4008Violação do protocolo: um quadro binário de tamanho ímpar ou grande demais.
4010Substituída por uma conexão mais nova à mesma sessão.

Enquanto a API reinicia, uma nova requisição de WebSocket pode receber HTTP 503. Tente de novo em instantes.

Retomada e substituição

Uma sessão sobrevive a uma queda de rede, a um deploy da API e a uma falha. Por 2 minutos após uma queda ela espera por você; depois disso, termina como disconnected e é salva.

  1. Chame POST /v1/live/sessions/:id/token. A resposta traz token, token_expires_at e websocket_url, os mesmos campos de quando você criou a sessão. A chamada falha com 409 session_ended depois que a sessão termina.
  2. Abra um novo WebSocket com esse token.
  3. session.started chega com resumed: true. offset_ms é a quantidade de áudio que a sessão já processou: continue com o áudio a partir desse ponto. Os ids das linhas continuam.

As linhas que já eram finais são mantidas. O trecho em andamento no momento da queda é perdido, então reenvie alguns segundos de áudio a partir de offset_ms se você o guarda em buffer. Os intervalos de reconexão não são cobrados.

A substituição é a mesma chamada. Se o servidor ainda acha que um socket está aberto, uma nova conexão válida o substitui, e o socket antigo é fechado com 4010 depois que suas linhas são salvas. O limite de sessões simultâneas nunca bloqueia uma retomada.

O SDK faz tudo isso por você: numa queda ou num evento reconnect, ele busca um token e reconecta com um intervalo crescente de 0.5 a 8 segundos por até 2 minutos, e reenvia os últimos 10 segundos de áudio enviados durante a queda.

Cobrança

A transcrição ao vivo usa o mesmo saldo de minutos das transcrições comuns.

  • O tempo conectado é cobrado, desde o momento em que o socket é aceito até ele fechar. O silêncio e um vídeo pausado contam. O intervalo entre uma queda e a retomada não conta.
  • Por minuto iniciado, com mínimo de 1. O primeiro minuto é cobrado quando a primeira conexão é aceita, e cada minuto seguinte quando ele começa.
  • Mais rápido que o tempo real é cobrado como tempo de áudio. Se o áudio enviado for mais longo que o tempo conectado, cobra-se o tempo do áudio.
  • A taxa é lida na API. Um minuto iniciado custa rate minutos de saldo, arredondados para cima. A taxa costuma ser 1 e pode ter decimais. Leia a taxa ao vivo em rates.live de GET /v1/account, em rate de GET /v1/live/capabilities e em rate da sessão que você cria. A taxa de uma sessão fica fixa depois de criada.
  • A tradução é gratuita.
  • Nunca conectada, nunca cobrada. Uma sessão que não se conecta em 5 minutos termina como not_connected.
  • Falhas são reembolsadas. Se uma sessão terminar com upstream_error ou internal_error antes de produzir uma única linha, todas as cobranças são devolvidas. Uma sessão vazia por causa do silêncio é cobrada e salva vazia.
  • Quando o saldo acaba e a recarga automática está ligada, o transcript1 cobra no cartão salvo enquanto a sessão continua transmitindo, e a sessão segue. Se a recarga automática estiver desligada ou pausada, ou se a cobrança falhar, a sessão é finalizada, salva e termina com balance_exhausted na virada de um minuto. O aviso balance_low vem antes, a menos que a recarga automática esteja ligada.

Criar uma sessão retorna 402 insufficient_minutes quando seu saldo utilizável é menor que um minuto iniciado na taxa atual. Com a recarga automática ligada, o transcript1 tenta recarregar primeiro. Veja Planos, minutos e limites.

Limites

FreeMonthlyAnnual
Sessões ao vivo simultâneas123
  • Uma sessão conta enquanto está em waiting, connected ou disconnected (dentro da janela de 2 minutos). Acima do limite, POST /v1/live/sessions retorna 429 too_many_live_sessions com Retry-After: 30.
  • As sessões ao vivo não contam para o limite de transcrições ativas.
  • Uma sessão dura no máximo 300 minutos de tempo conectado. Depois disso, termina com max_duration.
  • Uma sessão sem áudio por 5 minutos termina com idle_timeout; você recebe idle_timeout_soon aos 4 minutos. Uma transmissão pausada continua cobrando tempo conectado e conta como inativa, então encerre a sessão se a pausa for longa.
  • O transcript1 limita todas as sessões ao vivo em conjunto. Quando todas estão em uso, POST /v1/live/sessions retorna 503 live_capacity, que permite nova tentativa. Espere Retry-After (30 segundos) e tente de novo. Isso só é verificado ao criar uma sessão, nunca no meio de uma.
  • Criar sessões e buscar tokens contam para suas chamadas por minuto.
  • O áudio é somente PCM bruto: pcm_s16le, mono, de 8000 a 48000 Hz.

Capacidades

GET /v1/live/capabilities descreve o que a transcrição ao vivo aceita no momento, para que os clientes nunca fixem regras do provedor no código. O escopo necessário é transcriptions:read.

{
  "available": true,
  "auto_language": true,
  "languages": ["en", "es"],
  "translation": {
    "targets": ["es", "de"],
    "pairs": "any",
    "requires_language": false
  },
  "diarize": true,
  "audio_formats": ["pcm_s16le"],
  "sample_rate": { "min": 8000, "max": 48000 },
  "max_session_minutes": 300,
  "rate": 1
}

As listas estão abreviadas aqui. Leia-as na API.

  • translation.pairs é "any" (qualquer idioma detectado para qualquer destino) ou uma lista de objetos com from e to.
  • translation.requires_language é true quando a tradução exige um language explícito em vez de auto.
  • Quando a transcrição ao vivo não está configurada, a resposta é available: false com apenas rate, e criar uma sessão retorna 503 live_unavailable.

Com o SDK: await t1.live.capabilities().

Consultar uma sessão

GET /v1/live/sessions/:id retorna o estado de uma sessão. O escopo necessário é transcriptions:read.

curl https://api.transcript1.com/v1/live/sessions/lv_8fK2Lm0PqRsTuVwXyZaBcD \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
  "status": "ended",
  "end_reason": "client_ended",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "options": {
    "language": "en",
    "translate_to": "es",
    "diarize": false,
    "audio_format": "pcm_s16le",
    "sample_rate": 16000,
    "save": true
  },
  "connected_ms": 142500,
  "audio_ms": 141900,
  "minutes_charged": 3,
  "rate": 1,
  "created_at": "2026-11-02T11:58:12Z",
  "ended_at": "2026-11-02T12:00:35Z"
}

status é waiting, connected, disconnected ou ended. end_reason é null até a sessão terminar. Com o SDK: await t1.live.get(id).

Erros

Os erros de requisição usam o formato de erro de sempre. Estes são os que POST /v1/live/sessions e seus endpoints irmãos retornam:

Status HTTPcodePode repetir?Quando
422invalid_requestNãoUm campo está ausente ou inválido, por exemplo um sample_rate fora do intervalo de 8000 a 48000.
422unsupported_languageNãolanguage não é aceito na transcrição ao vivo.
422unsupported_translationNãoO idioma de destino, ou o par de idiomas, não é aceito.
402insufficient_minutesNãoSeu saldo utilizável é menor que um minuto iniciado na taxa atual.
403email_not_verifiedNãoConfirme primeiro o seu e-mail.
429too_many_live_sessionsSimTodas as sessões ao vivo simultâneas do seu plano estão em uso. Espere Retry-After.
503live_capacitySimToda a capacidade ao vivo está em uso. Espere Retry-After (30 segundos).
503live_unavailableNãoA transcrição ao vivo não está disponível no momento.
409session_endedNãoPOST /v1/live/sessions/:id/token para uma sessão que já terminou.

Os problemas no socket chegam como eventos error e códigos de fechamento.

Transcrições salvas

Com save: true (o padrão), a sessão é salva como uma transcrição comum quando termina. O áudio nunca é armazenado.

  • transcription_id é devolvido quando você cria a sessão. O status é live enquanto a sessão roda e completed depois que ela termina. Uma sessão que nunca se conectou deixa uma transcrição canceled.
  • A transcrição tem source: { "platform": "live", "session_id": "lv_…" }. Não há link nem arquivo. O painel a lista como uma sessão ao vivo.
  • Cada linha vira um segmento, com o mesmo índice do id da linha. Leia a transcrição em JSON, texto, SRT ou VTT como qualquer outra: veja a referência da API.
  • A tradução ao vivo é salva junto. Leia-a com o parâmetro translation, como descrito em Tradução.
  • Adicione mais idiomas de tradução depois com POST /v1/transcriptions/:id/translations, quando a transcrição estiver completed. Veja Tradução.

Com save: false, nada é mantido depois que a sessão termina: não há transcrição e transcription_id é null. Você ainda recebe todas as linhas pelo socket, e a cobrança de sempre se aplica.

Segurança

  • O token que você recebe ao criar uma sessão é de uso único e expira em 60 segundos. Ele é enviado como subprotocolo do WebSocket, não na URL, então fica fora dos logs de acesso. Um token vazado não serve de nada depois de usado ou expirado.
  • Use uma chave de API apenas em um servidor que você controla. Nunca coloque uma chave de API em uma página web nem em uma extensão.
  • Em um navegador ou extensão, faça o login das pessoas com OAuth e passe o token de acesso OAuth para getAccessToken de createLiveClient. O token precisa do escopo transcriptions:write para iniciar sessões. Os tokens de acesso OAuth expiram em 1 hora, e cada retomada pede um novo a getAccessToken.
  • Seu servidor também pode criar a sessão com a chave de API e entregar ao navegador só websocket_url e token. O cliente então se conecta diretamente, mas não consegue retomar sem um novo token seu.

Última atualização 3 de novembro de 2026