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 quando | Use 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:
| Campo | Padrão | Regra |
|---|---|---|
language | auto | auto, ou um código de duas letras que a transcrição ao vivo aceite. |
translate_to | nenhum | Um código de duas letras. Um único idioma de destino por sessão. Veja Traduzir ao vivo. |
diarize | false | Identifica os falantes. Algumas configurações não conseguem, por exemplo junto com language: "auto", e respondem 422. Confira as capacidades. |
audio_format | pcm_s16le | O único valor. O campo existe para que outros formatos possam ser adicionados depois. |
sample_rate | 16000 | Um inteiro de 8000 a 48000. Somente mono. |
save | true | Com 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
linetem um original (text,final) e um objetotranslation(language,text,final).translationénullquandotranslate_tonã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
| Mensagem | Significado |
|---|---|
| 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 coisa | Um 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.
| Evento | Enviado quando |
|---|---|
session.started | A conexão foi aceita. Envie áudio a partir de agora. |
line | Uma linha de legenda foi criada ou alterada. |
usage | Depois de cada cobrança. |
warning | Algo pede atenção, mas a sessão continua. |
error | Um problema não fatal, ou o motivo de um fechamento imediato. |
reconnect | O servidor está prestes a reiniciar. Retome a sessão. |
session.ended | A 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:
code | Significado |
|---|---|
balance_low | A 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_dropped | Chegou á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_soon | Sem á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:
code | Fatal | Significado |
|---|---|---|
invalid_message | Não | Um quadro de texto que não é finalize nem end. |
invalid_token | Sim | O token é inválido, expirou ou já foi usado. O socket fecha com 4001. |
not_found | Sim | A sessão não existe. O socket fecha com 4004. |
session_ended | Sim | A 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
| Campo | Significado |
|---|---|
id | Um 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_ms | Início e fim no tempo de áudio da sessão. Esse tempo continua após as reconexões. |
speaker | O rótulo do falante quando diarize está ativado; caso contrário, null. Os rótulos são os mesmos das transcrições comuns. |
language | O idioma detectado ou informado da linha, ou null enquanto ainda não é conhecido. |
text | O texto original até agora. |
final | true quando o texto original não vai mais mudar. |
translation | null 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:
| Motivo | Significado |
|---|---|
client_ended | Você enviou end. |
balance_exhausted | A cobrança seguinte falhou. A sessão termina na virada de um minuto e é salva. |
idle_timeout | Sem áudio por 5 minutos. |
max_duration | A sessão ficou conectada por 300 minutos. Inicie outra. |
disconnected | O cliente ficou ausente por mais de 2 minutos sem retomar. |
upstream_error | O provedor de voz falhou. |
internal_error | Tivemos um erro interno. |
not_connected | A sessão nunca se conectou em 5 minutos. Nunca é cobrada e só aparece por GET. |
Códigos de fechamento
| Código | Significado |
|---|---|
1000 | Fechamento normal. Sempre vem depois de session.ended. |
1011 | Erro inesperado do servidor. O servidor recupera a sessão: retome com um token novo. |
1012 | Reinício do servidor. Vem depois de reconnect: retome com um token novo. |
4001 | O token é inválido, expirou ou já foi usado. |
4004 | A sessão não foi encontrada ou já terminou. |
4008 | Violação do protocolo: um quadro binário de tamanho ímpar ou grande demais. |
4010 | Substituí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.
- Chame
POST /v1/live/sessions/:id/token. A resposta traztoken,token_expires_atewebsocket_url, os mesmos campos de quando você criou a sessão. A chamada falha com409 session_endeddepois que a sessão termina. - Abra um novo WebSocket com esse token.
session.startedchega comresumed: 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
rateminutos de saldo, arredondados para cima. A taxa costuma ser 1 e pode ter decimais. Leia a taxa ao vivo emrates.livedeGET /v1/account, emratedeGET /v1/live/capabilitiese emrateda 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_errorouinternal_errorantes 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_exhaustedna virada de um minuto. O avisobalance_lowvem 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
| Free | Monthly | Annual | |
|---|---|---|---|
| Sessões ao vivo simultâneas | 1 | 2 | 3 |
- Uma sessão conta enquanto está em
waiting,connectedoudisconnected(dentro da janela de 2 minutos). Acima do limite,POST /v1/live/sessionsretorna429 too_many_live_sessionscomRetry-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ê recebeidle_timeout_soonaos 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/sessionsretorna503 live_capacity, que permite nova tentativa. EspereRetry-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 comfrometo.translation.requires_languageétruequando a tradução exige umlanguageexplícito em vez deauto.- Quando a transcrição ao vivo não está configurada, a resposta é
available: falsecom apenasrate, e criar uma sessão retorna503 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 HTTP | code | Pode repetir? | Quando |
|---|---|---|---|
| 422 | invalid_request | Não | Um campo está ausente ou inválido, por exemplo um sample_rate fora do intervalo de 8000 a 48000. |
| 422 | unsupported_language | Não | language não é aceito na transcrição ao vivo. |
| 422 | unsupported_translation | Não | O idioma de destino, ou o par de idiomas, não é aceito. |
| 402 | insufficient_minutes | Não | Seu saldo utilizável é menor que um minuto iniciado na taxa atual. |
| 403 | email_not_verified | Não | Confirme primeiro o seu e-mail. |
| 429 | too_many_live_sessions | Sim | Todas as sessões ao vivo simultâneas do seu plano estão em uso. Espere Retry-After. |
| 503 | live_capacity | Sim | Toda a capacidade ao vivo está em uso. Espere Retry-After (30 segundos). |
| 503 | live_unavailable | Não | A transcrição ao vivo não está disponível no momento. |
| 409 | session_ended | Não | POST /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 éliveenquanto a sessão roda ecompleteddepois que ela termina. Uma sessão que nunca se conectou deixa uma transcriçãocanceled.- 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
idda 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 estivercompleted. 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
getAccessTokendecreateLiveClient. O token precisa do escopotranscriptions:writepara iniciar sessões. Os tokens de acesso OAuth expiram em 1 hora, e cada retomada pede um novo agetAccessToken. - Seu servidor também pode criar a sessão com a chave de API e entregar ao navegador só
websocket_urletoken. 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