DocumentaciónTranscripción en vivo

Transcripción en vivo

Envía audio del micrófono o de una pestaña a transcript1 por WebSocket y recibe líneas de subtítulos, con traducción opcional en tiempo real, mientras la gente habla.

La transcripción en vivo convierte un flujo de audio en líneas de subtítulos mientras la gente todavía habla. Tu cliente envía audio PCM sin comprimir por un WebSocket, y transcript1 devuelve líneas que empiezan como una primera aproximación y luego pasan a ser finales. Agrega translate_to y cada línea incluye también su traducción. Cuando termina la sesión, el resultado se guarda como una transcripción normal.

No hay planes aparte para el modo en vivo: usa el mismo saldo de minutos que todo lo demás. En Facturación verás cómo se cuenta el tiempo conectado.

Cuándo usar la transcripción en vivo

Usa el modo en vivo cuandoUsa una transcripción normal cuando
El audio todavía se está produciendo: una llamada, una charla, una transmisión, un video que estás viendo.Tienes un enlace o un archivo terminado.
Necesitas subtítulos en uno o dos segundos tras la voz.Quieres la mayor precisión y puedes esperar el resultado.
Quieres traducción en tiempo real junto a cada línea.Quieres traducir una transcripción ya terminada, o a varios idiomas.

El modo en vivo solo acepta audio, como PCM sin comprimir. No admite enlaces de video, archivos subidos, WebM, Opus ni MP3. Captura el audio en tu cliente y conviértelo antes a PCM. En Captura de audio en el navegador se muestra cómo.

Inicio rápido con el SDK

Instala el SDK 1.5.0 o posterior: npm i @transcript1/sdk@latest. t1.live.start crea la sesión, se conecta y se resuelve cuando la sesión ya empezó, así que puedes enviar audio de inmediato.

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);

Las líneas se actualizan en su lugar. Reemplaza la línea que ya tienes cuando llegue otra con el mismo id, o lee todas desde live.lines. end() termina la sesión, espera las últimas líneas y guarda la transcripción. Consulta todos los métodos y eventos en SDK para Node.js.

Inicio rápido con un WebSocket directo

Sin el SDK hay dos pasos: crear una sesión por HTTPS y abrir el WebSocket con el token de un solo uso que recibes.

Crea una sesión

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}'

La 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 es null cuando save es false. El token es de un solo uso y vence a los 60 segundos, así que conéctate de inmediato. rate es la cantidad de minutos de saldo que se cobra por cada minuto iniciado de esta sesión; consulta Facturación.

El cuerpo de la solicitud acepta estos campos. Todos son opcionales, y el permiso necesario es transcriptions:write:

CampoValor por defectoRegla
languageautoauto, o un código de dos letras que admita la transcripción en vivo.
translate_toningunoUn código de dos letras. Un solo idioma de destino por sesión. Consulta Traducir en vivo.
diarizefalseIdentifica a los hablantes. Algunas configuraciones no pueden hacerlo, por ejemplo junto con language: "auto", y responden 422. Consulta las capacidades.
audio_formatpcm_s16leEs el único valor. El campo existe para poder añadir formatos más adelante.
sample_rate16000Un entero de 8000 a 48000. Solo mono.
savetrueCon false no se conserva nada cuando termina la sesión. Consulta Transcripciones guardadas.

Conéctate y transmite

Abre el WebSocket con el token como segundo subprotocolo. Así funciona en los navegadores y en Node 22+ sin encabezados personalizados. Espera session.started antes de enviar audio: los fragmentos binarios enviados antes se descartan y recibes una advertencia 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));

En Node 20 no hay WebSocket integrado: usa el paquete ws, que tiene la misma forma new WebSocket(url, protocols).

El servidor responde a un token válido con el subprotocolo t1-live y nada más. Un token incorrecto nunca hace fallar el handshake: el socket se abre, recibes un evento error y el socket se cierra con un código 4xxx, de modo que un navegador muestra el motivo real en lugar de un 1006 genérico. Consulta Códigos de cierre.

Captura de audio en el navegador

El audio en vivo debe ser PCM mono de 16 bits con signo, little-endian, a la sample_rate de la sesión. Los navegadores entregan muestras en coma flotante, así que conviértelas en un AudioWorklet. Este fragmento captura el micrófono como PCM de 16 kHz en fragmentos de 100 ms y lo envía con el punto de entrada del SDK para navegador. getAccessToken devuelve un token de acceso OAuth; nunca pongas una clave de API en un navegador. Consulta Seguridad.

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();
}

El worklet está escrito para 16 kHz. Si el contexto funciona a otra frecuencia, el fragmento ya no dura 100 ms, pero el PCM sigue siendo correcto porque sampleRate coincide con el del contexto. Para capturar el sonido de una pestaña o de un video en lugar del micrófono, crea el MediaStream con getDisplayMedia o con el captureStream() de un elemento multimedia y conéctalo igual.

Traducir en vivo

Agrega translate_to a la sesión y transcript1 traduce cada línea a medida que se dice.

  • Un solo idioma de destino por sesión. Después puedes agregar más idiomas a la transcripción guardada con el endpoint de traducciones.
  • Cada line tiene un original (text, final) y un objeto translation (language, text, final). translation es null si no definiste translate_to.
  • El original y la traducción pasan a ser finales por separado: una línea puede tener el original final mientras su traducción todavía cambia. Muestra una línea como final cuando lo sean ambos.
  • Una línea que ya está en el idioma de destino usa su propio texto como traducción, y es final cuando la línea se cierra.
  • Una línea se cierra cuando el hablante hace una pausa, cambia el hablante, una pausa entre palabras dura más de 0.9 segundos, la línea llega a dos filas de subtítulo (84 caracteres) o a 15 segundos, o envías finalize. Con la traducción activada, una línea puede tardar unos 3 segundos más en cerrarse mientras llega su traducción. En ese caso la traducción puede seguir abierta (final: false) y seguir creciendo después de que el original ya es final.
  • La traducción es gratis. No usa minutos.

Los idiomas de destino y los pares de idiomas disponibles dependen del proveedor de voz en vivo. Consulta las capacidades en lugar de fijar reglas en tu código. Un destino no admitido devuelve 422 unsupported_translation. Por ejemplo, un proveedor puede exigir un language explícito en lugar de auto, o permitir solo pares que incluyan inglés.

Protocolo WebSocket

Conexión

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

El token viaja como subprotocolo de WebSocket. Nunca aparece en una URL ni en un registro de acceso. No envíes un encabezado Authorization: el token es la credencial. El servidor envía un ping cada 30 segundos; los navegadores y el paquete ws responden solos. Un socket que no envía pong durante 60 segundos se desconecta.

Una conexión válida a una sesión que ya tiene un socket reemplaza al anterior. Consulta Reanudación y reemplazo.

Mensajes del cliente

MensajeSignificado
Fragmento binarioAudio: PCM s16le mono a la sample_rate de la sesión. Cada fragmento tiene una longitud par y como máximo 256 KB. Unos 100 ms por fragmento es un buen tamaño.
{"type":"finalize"}Cierra ya la línea actual, por ejemplo cuando pausas el video.
{"type":"end"}Termina la sesión y la guarda. El servidor responde session.ended y cierra el socket.
Cualquier otra cosaUn evento error no fatal con el código invalid_message.

Un fragmento de longitud impar o demasiado grande es una violación del protocolo y cierra el socket con 4008.

Empieza a enviar audio después de session.started. Un cliente que envía audio más rápido que el tiempo real paga el tiempo del audio en lugar del tiempo conectado (consulta Facturación), así que envíalo a la velocidad a la que se produce.

Eventos del servidor

Cada evento es un mensaje de texto JSON con un type.

EventoCuándo se envía
session.startedSe aceptó la conexión. Envía audio a partir de ahora.
lineSe creó o cambió una línea de subtítulos.
usageDespués de cada cobro.
warningAlgo requiere atención, pero la sesión continúa.
errorUn problema no fatal, o el motivo de un cierre inmediato.
reconnectEl servidor está por reiniciarse. Reanuda la sesión.
session.endedLa sesión terminó y se guardó. Es el último evento antes de un cierre normal.

session.started indica si se trata de una reanudación y cuánto audio ha procesado ya la sesión:

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

line trae el estado completo y actual de una línea, así que reemplaza la que tienes con el mismo id. Consulta El objeto de línea.

{
  "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 se envía después de cada cobro, con el tiempo conectado, el tiempo del audio, los minutos de saldo cobrados hasta ahora y los minutos que te quedan:

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

warning incluye un code:

codeSignificado
balance_lowEl próximo cobro fallaría. La sesión termina con balance_exhausted cuando llegue ese momento. No se envía mientras la recarga automática está activada.
audio_droppedLlegó audio antes de que la sesión estuviera lista, o demasiado audio mientras se conectaba el proveedor de voz. Se descartó el audio más antiguo.
idle_timeout_soonLlevas 4 minutos sin audio. La sesión termina tras 5 minutos sin audio. Envía audio para conservarla.
{ "type": "warning", "code": "idle_timeout_soon" }

error incluye un code y un message. Estos códigos pueden llegar por un socket en vivo:

codeFatalSignificado
invalid_messageNoUn mensaje de texto que no es finalize ni end.
invalid_tokenSíEl token no es válido, venció o ya se usó. El socket se cierra con 4001.
not_foundSíLa sesión no existe. El socket se cierra con 4004.
session_endedSíLa sesión ya terminó. El socket se cierra con 4004.
{ "type": "error", "code": "invalid_message", "message": "Expected {\"type\":\"finalize\"} or {\"type\":\"end\"}" }

reconnect llega antes de que el servidor cierre el socket con 1012, por ejemplo durante un despliegue. Obtén un token nuevo y reanuda.

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

session.ended es el último evento de toda sesión. Las cifras son los totales finales:

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

El objeto de línea

CampoSignificado
idUn número que empieza en 0 y crece de uno en uno con cada línea. Es el índice del segmento en la transcripción guardada.
start_ms, end_msInicio y fin en tiempo de audio de la sesión. Este tiempo continúa tras las reconexiones.
speakerLa etiqueta del hablante cuando diarize está activado; si no, null. Las etiquetas son las mismas que en las transcripciones normales.
languageEl idioma detectado o indicado de la línea, o null si aún no se conoce.
textEl texto original hasta ahora.
finaltrue cuando el texto original ya no cambiará.
translationnull sin translate_to; en caso contrario, language, text y final.

Muestra las líneas con final: false con un estilo más tenue: su texto todavía puede cambiar. El texto de una línea con final: true no vuelve a cambiar, y una línea final nunca se elimina.

El SDK usa camelCase para los campos de línea: startMs, endMs.

Motivos de finalización

session.ended y GET /v1/live/sessions/:id devuelven un end_reason:

MotivoSignificado
client_endedEnviaste end.
balance_exhaustedFalló el siguiente cobro. La sesión termina en un límite de minuto y se guarda.
idle_timeoutPasaron 5 minutos sin audio.
max_durationLa sesión estuvo conectada 300 minutos. Inicia una nueva.
disconnectedEl cliente estuvo ausente más de 2 minutos sin reanudar.
upstream_errorFalló el proveedor de voz.
internal_errorFalló algo en transcript1.
not_connectedLa sesión nunca se conectó en 5 minutos. Nunca se cobra y solo se ve mediante GET.

Códigos de cierre

CódigoSignificado
1000Cierre normal. Siempre va después de session.ended.
1011Error inesperado del servidor. El servidor recupera la sesión: reanuda con un token nuevo.
1012Reinicio del servidor. Va después de reconnect: reanuda con un token nuevo.
4001El token no es válido, venció o ya se usó.
4004No se encontró la sesión o ya terminó.
4008Violación del protocolo: un fragmento binario de longitud impar o demasiado grande.
4010Reemplazada por una conexión más nueva a la misma sesión.

Mientras la API se reinicia, una nueva solicitud de WebSocket puede recibir HTTP 503. Vuelve a intentarlo en un momento.

Reanudación y reemplazo

Una sesión sobrevive a una caída de la red, a un despliegue de la API y a un fallo. Durante 2 minutos después de una desconexión te espera; pasado ese tiempo termina como disconnected y se guarda.

  1. Llama a POST /v1/live/sessions/:id/token. La respuesta trae token, token_expires_at y websocket_url, los mismos campos que al crear la sesión. Falla con 409 session_ended si la sesión ya terminó.
  2. Abre un WebSocket nuevo con ese token.
  3. Llega session.started con resumed: true. offset_ms es la cantidad de audio que la sesión ya procesó: continúa con el audio desde ese punto. Los ids de línea continúan.

Las líneas que ya eran finales se conservan. Se pierde el tramo en curso en el momento de la caída, así que reenvía unos segundos de audio desde offset_ms si lo tienes en un búfer. Los cortes de reconexión no se cobran.

El reemplazo se hace con la misma llamada. Si el servidor aún cree que hay un socket abierto, una conexión válida nueva lo reemplaza y el socket anterior se cierra con 4010 después de guardar sus líneas. El límite de sesiones simultáneas nunca bloquea una reanudación.

El SDK hace todo esto por ti: ante una caída o un evento reconnect, obtiene un token y se reconecta con una espera de 0.5 a 8 segundos durante un máximo de 2 minutos, y reenvía los últimos 10 segundos de audio enviados durante el corte.

Facturación

La transcripción en vivo usa el mismo saldo de minutos que las transcripciones por lotes.

  • Se factura el tiempo conectado, desde que se acepta el socket hasta que se cierra. El silencio y un video en pausa cuentan. El tiempo entre una caída y la reanudación no cuenta.
  • Por minuto iniciado, con un mínimo de 1. El primer minuto se cobra cuando se acepta la primera conexión, y cada minuto siguiente cuando empieza.
  • Más rápido que el tiempo real se factura como tiempo de audio. Si el audio que envías dura más que el tiempo conectado, se factura el tiempo del audio.
  • La tarifa se consulta en la API. Un minuto iniciado cuesta rate minutos de saldo, redondeados hacia arriba. La tarifa suele ser 1 y puede tener decimales. Consulta la tarifa en vivo en rates.live de GET /v1/account, en rate de GET /v1/live/capabilities y en rate de la sesión que creas. La tarifa de una sesión queda fija una vez creada.
  • La traducción es gratis.
  • Si nunca se conecta, no se cobra. Una sesión que no se conecta en 5 minutos termina como not_connected.
  • Los fallos se reembolsan. Si una sesión termina con upstream_error o internal_error antes de producir una sola línea, se devuelve todo lo cobrado. Una sesión vacía por falta de voz se cobra y se guarda vacía.
  • Cuando se acaba el saldo y la recarga automática está activada, transcript1 cobra a tu tarjeta guardada mientras la sesión sigue transmitiendo, y la sesión continúa. Si la recarga automática está desactivada o en pausa, o el cobro falla, la sesión se finaliza, se guarda y termina con balance_exhausted en un límite de minuto. Antes llega la advertencia balance_low, salvo que la recarga automática esté activada.

Crear una sesión devuelve 402 insufficient_minutes cuando tu saldo utilizable es menor que un minuto iniciado a la tarifa actual. Si la recarga automática está activada, transcript1 intenta recargar primero. Consulta Facturación y límites.

Límites

FreeMonthlyAnnual
Sesiones en vivo simultáneas123
  • Una sesión cuenta mientras está en waiting, connected o disconnected (dentro de su ventana de 2 minutos). Si superas el límite, POST /v1/live/sessions devuelve 429 too_many_live_sessions con Retry-After: 30.
  • Las sesiones en vivo no cuentan para el límite de transcripciones simultáneas.
  • Una sesión dura como máximo 300 minutos de tiempo conectado. Después termina con max_duration.
  • Una sesión sin audio durante 5 minutos termina con idle_timeout; recibes idle_timeout_soon a los 4 minutos. Una transmisión en pausa sigue facturando tiempo conectado y cuenta como inactiva, así que termina la sesión si la pausa es larga.
  • transcript1 limita todas las sesiones en vivo en conjunto. Cuando están todas en uso, POST /v1/live/sessions devuelve 503 live_capacity, que se puede reintentar. Espera Retry-After (30 segundos) y vuelve a intentarlo. Esto solo se comprueba al crear una sesión, nunca en mitad de una.
  • Crear sesiones y obtener tokens cuentan para tus requests por minuto.
  • El audio es solo PCM sin comprimir: pcm_s16le, mono, de 8000 a 48000 Hz.

Capacidades

GET /v1/live/capabilities describe qué admite ahora la transcripción en vivo, para que los clientes nunca fijen reglas del proveedor en su código. El permiso necesario es 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
}

Aquí las listas están abreviadas. Léelas desde la API.

  • translation.pairs es "any" (cualquier idioma detectado hacia cualquier destino), o una lista de objetos con from y to.
  • translation.requires_language es true cuando la traducción necesita un language explícito en lugar de auto.
  • Cuando la transcripción en vivo no está configurada, la respuesta es available: false con solo rate, y crear una sesión devuelve 503 live_unavailable.

Con el SDK: await t1.live.capabilities().

Consultar una sesión

GET /v1/live/sessions/:id devuelve el estado de una sesión. El permiso necesario es 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 es waiting, connected, disconnected o ended. end_reason es null hasta que la sesión termina. Con el SDK: await t1.live.get(id).

Errores

Los errores de solicitud usan el formato de error habitual. Estos son los que devuelven POST /v1/live/sessions y sus endpoints hermanos:

Estado HTTPcode¿Reintentar?Cuándo
422invalid_requestNoFalta un campo o no es válido, por ejemplo un sample_rate fuera del rango de 8000 a 48000.
422unsupported_languageNolanguage no está admitido para la transcripción en vivo.
422unsupported_translationNoEl idioma de destino, o el par de idiomas, no está admitido.
402insufficient_minutesNoTu saldo utilizable es menor que un minuto iniciado a la tarifa actual.
403email_not_verifiedNoVerifica primero tu correo.
429too_many_live_sessionsSíYa usas todas las sesiones en vivo simultáneas de tu plan. Espera Retry-After.
503live_capacitySíToda la capacidad en vivo está ocupada. Espera Retry-After (30 segundos).
503live_unavailableNoLa transcripción en vivo no está disponible por ahora.
409session_endedNoPOST /v1/live/sessions/:id/token para una sesión que ya terminó.

Los problemas en el socket llegan como eventos error y códigos de cierre.

Transcripciones guardadas

Con save: true (el valor por defecto), la sesión se guarda como una transcripción normal cuando termina. El audio nunca se almacena.

  • transcription_id se devuelve al crear la sesión. Su estado es live mientras la sesión está activa y completed cuando termina. Una sesión que nunca se conectó deja una transcripción canceled.
  • La transcripción tiene source: { "platform": "live", "session_id": "lv_…" }. No hay enlace ni archivo. El panel la muestra como sesión en vivo.
  • Cada línea se convierte en un segmento, con el mismo índice que el id de la línea. Lee la transcripción como JSON, texto, SRT o VTT igual que cualquier otra: consulta la referencia de la API.
  • La traducción en vivo se guarda con ella. Léela con el parámetro translation, como se explica en Traducción.
  • Más adelante puedes agregar idiomas de traducción con POST /v1/transcriptions/:id/translations, cuando la transcripción esté en completed. Consulta Traducción.

Con save: false no se conserva nada cuando termina la sesión: no hay transcripción y transcription_id es null. Igualmente recibes todas las líneas por el socket y se aplica el cobro habitual.

Seguridad

  • El token que recibes al crear una sesión es de un solo uso y vence a los 60 segundos. Se envía como subprotocolo de WebSocket, no en la URL, así que no queda en los registros de acceso. Un token filtrado no sirve una vez usado o vencido.
  • Usa una clave de API solo desde un servidor que controles. Nunca pongas una clave de API en una página web ni en una extensión.
  • En un navegador o una extensión, inicia sesión de las personas con OAuth y pasa el token de acceso OAuth a getAccessToken de createLiveClient. El token necesita el permiso transcriptions:write para iniciar sesiones. Los tokens de acceso OAuth vencen a la hora, y cada reanudación pide uno nuevo a getAccessToken.
  • Tu servidor también puede crear la sesión con su clave de API y entregar al navegador solo websocket_url y token. Entonces el cliente se conecta directamente, pero no puede reanudar sin un token nuevo que le des tú.

Última actualización 3 de noviembre de 2026