ДокументацияРасшифровка в реальном времени
Расшифровка в реальном времени
Отправляйте звук с микрофона или вкладки в transcript1 по WebSocket и получайте строки субтитров, при желании с переводом, прямо во время речи.
Расшифровка в реальном времени превращает аудиопоток в строки субтитров, пока люди ещё говорят. Клиент отправляет по WebSocket сырой PCM-звук, а transcript1 возвращает строки: сначала это черновой вариант, затем окончательный текст. Добавьте translate_to, и у каждой строки появится перевод. После завершения сессии результат сохраняется как обычная транскрибация.
Отдельных тарифов для live нет: расшифровка в реальном времени списывает минуты с того же баланса, что и всё остальное. Как считается время подключения, читайте в разделе «Оплата».
Когда нужен live
| Выбирайте live, если | Выбирайте обычную транскрибацию, если |
|---|---|
| Звук ещё создаётся: звонок, доклад, трансляция, видео, которое вы смотрите. | У вас есть ссылка или готовый файл. |
| Субтитры нужны через секунду-две после слов. | Важна максимальная точность, а результат можно подождать. |
| Рядом с каждой строкой нужен перевод в реальном времени. | Нужен перевод готового транскрипта, в том числе на несколько языков. |
Live принимает только звук в виде сырого PCM. Ссылки на видео, загруженные файлы, WebM, Opus и MP3 не поддерживаются: запишите звук на стороне клиента и сначала преобразуйте его в PCM. Как это сделать, показано в разделе «Захват звука в браузере».
Быстрый старт с SDK
Установите SDK версии 1.5.0 или новее: npm i @transcript1/sdk@latest. Метод t1.live.start создаёт сессию, подключается и возвращает результат, когда сессия уже началась, поэтому звук можно отправлять сразу.
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);Строки обновляются на месте. Когда приходит строка с уже известным id, замените ею прежнюю или просто читайте все строки из live.lines. Метод end() завершает сессию, дожидается последних строк и сохраняет транскрипт. Все методы и события описаны в разделе «SDK для Node.js».
Быстрый старт с чистым WebSocket
Без SDK нужны два шага: создать сессию по HTTPS, затем открыть WebSocket с одноразовым токеном из ответа.
Создание сессии
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}'API отвечает кодом 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
}Если save равен false, transcription_id будет null. Токен одноразовый и действует 60 секунд, поэтому подключайтесь сразу. rate — сколько минут баланса списывается за одну начатую минуту этой сессии; подробнее в разделе «Оплата».
В теле запроса можно передать такие поля. Все они необязательны, нужное разрешение — transcriptions:write:
| Поле | По умолчанию | Правило |
|---|---|---|
language | auto | auto или двухбуквенный код языка, который поддерживает live. |
translate_to | нет | Двухбуквенный код языка. На сессию приходится один язык перевода. См. «Перевод в реальном времени». |
diarize | false | Подписывать говорящих. В некоторых конфигурациях разметка недоступна, например вместе с language: "auto", и API отвечает 422. Проверьте возможности. |
audio_format | pcm_s16le | Единственное значение. Поле нужно, чтобы позже можно было добавить другие форматы. |
sample_rate | 16000 | Целое число от 8000 до 48000. Только моно. |
save | true | При false после завершения сессии ничего не хранится. См. «Сохранённые транскрипты». |
Подключение и передача звука
Откройте WebSocket, передав токен вторым субпротоколом. Так можно подключаться из браузера и из Node 22+ без собственных заголовков. Дождитесь session.started, прежде чем отправлять звук: бинарные кадры, отправленные раньше, отбрасываются, а вы получите предупреждение 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));В Node 20 встроенного WebSocket нет: используйте пакет ws с той же формой вызова new WebSocket(url, protocols).
На корректный токен сервер отвечает только субпротоколом t1-live. Неверный токен не обрывает рукопожатие: сокет открывается, вы получаете событие error, и сокет закрывается с кодом 4xxx. Благодаря этому браузер показывает настоящую причину, а не общий код 1006. См. «Коды закрытия».
Захват звука в браузере
Звук для live должен быть знаковым 16-битным моно PCM с порядком байтов little-endian и частотой sample_rate сессии. Браузер отдаёт сэмплы с плавающей точкой, поэтому преобразуйте их в AudioWorklet. Пример ниже захватывает микрофон как PCM 16 кГц блоками по 100 мс и отправляет его через браузерную точку входа SDK. getAccessToken возвращает OAuth-токен доступа; никогда не кладите API-ключ в браузер. См. «Безопасность».
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();
}Скрипт AudioWorklet рассчитан на 16 кГц. Если контекст работает на другой частоте, блок перестаёт быть равным 100 мс, но PCM остаётся корректным, потому что sampleRate совпадает с контекстом. Чтобы захватить звук вкладки или видео вместо микрофона, создайте MediaStream через getDisplayMedia или из captureStream() медиаэлемента и подключите его так же.
Перевод в реальном времени
Добавьте в сессию translate_to, и transcript1 будет переводить каждую строку по мере произнесения.
- На сессию приходится один язык перевода. Другие языки можно добавить к сохранённому транскрипту позже через эндпоинт перевода.
- В каждой строке
lineесть оригинал (text,final) и объектtranslation(language,text,final). Еслиtranslate_toне задан,translationравенnull. - Оригинал и перевод становятся окончательными независимо друг от друга: у строки оригинал уже может быть финальным, а перевод ещё меняться. Показывайте строку как финальную, когда окончательны оба.
- Если строка уже на целевом языке, её собственный текст служит переводом и становится финальным, когда строка закрывается.
- Строка закрывается, когда говорящий делает паузу, меняется говорящий, пауза между словами превышает 0.9 секунды, строка достигает двух строк субтитров (84 символа) или 15 секунд, либо вы отправляете
finalize. При включённом переводе закрытие может занять примерно на 3 секунды больше, пока приходит перевод. Тогда перевод может оставаться незавершённым (final: false) и продолжать расти, даже когда оригинал уже финальный. - Перевод бесплатный и минуты не расходует.
Какие языки перевода и какие пары доступны, зависит от поставщика распознавания речи в реальном времени. Читайте возможности, а не зашивайте правила в код. Неподдерживаемый язык перевода вернёт 422 unsupported_translation. Например, поставщик может требовать явный language вместо auto или разрешать только пары с английским.
Протокол WebSocket
Подключение
new WebSocket(websocket_url, ['t1-live', token]);Токен передаётся как субпротокол WebSocket. Он не попадает ни в URL, ни в журналы доступа. Заголовок Authorization не отправляйте: учётными данными служит сам токен. Сервер шлёт ping каждые 30 секунд; браузеры и пакет ws отвечают автоматически. Сокет, который не ответил pong за 60 секунд, отключается.
Корректное подключение к сессии, у которой уже есть сокет, заменяет старый. См. «Возобновление и перехват».
Сообщения клиента
| Сообщение | Значение |
|---|---|
| Бинарный кадр | Звук: PCM s16le, моно, с sample_rate сессии. Длина кадра чётная, размер не больше 256 KB. Хороший размер — около 100 мс звука на кадр. |
{"type":"finalize"} | Закрыть текущую строку сейчас, например при паузе видео. |
{"type":"end"} | Завершить сессию и сохранить её. Сервер отвечает session.ended и закрывает сокет. |
| Любое другое | Нефатальное событие error с кодом invalid_message. |
Кадр нечётной длины или слишком большого размера считается нарушением протокола и закрывает сокет с кодом 4008.
Начинайте отправлять звук после session.started. Если клиент отправляет звук быстрее реального времени, оплачивается длительность звука, а не время подключения (см. «Оплата»), поэтому отправляйте звук с той скоростью, с какой он создаётся.
События сервера
Каждое событие — это текстовый JSON-кадр с полем type.
| Событие | Когда приходит |
|---|---|
session.started | Подключение принято. С этого момента отправляйте звук. |
line | Строка субтитров создана или изменилась. |
usage | После каждого списания. |
warning | Нужно обратить внимание, но сессия продолжается. |
error | Нефатальная проблема или причина немедленного закрытия. |
reconnect | Сервер скоро перезапустится. Возобновите сессию. |
session.ended | Сессия завершена и сохранена. Последнее событие перед обычным закрытием. |
В session.started указано, возобновлена ли сессия и сколько звука она уже обработала:
{
"type": "session.started",
"session_id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
"transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
"resumed": false,
"offset_ms": 0
}line содержит полное текущее состояние одной строки, поэтому заменяйте имеющуюся строку с тем же id. См. «Объект строки».
{
"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 приходит после каждого списания: время подключения, длительность звука, списанные к этому моменту минуты баланса и сколько минут осталось:
{
"type": "usage",
"connected_ms": 61240,
"audio_ms": 60100,
"minutes_charged": 2,
"minutes_remaining": 898
}У warning есть code:
code | Значение |
|---|---|
balance_low | Следующее списание не пройдёт. Когда оно наступит, сессия завершится с balance_exhausted. Не отправляется, пока включено автопополнение. |
audio_dropped | Звук пришёл до готовности сессии или его пришло слишком много, пока подключался поставщик распознавания. Самый старый звук отброшен. |
idle_timeout_soon | Звука нет уже 4 минуты. Сессия закончится через 5 минут без звука. Отправьте звук, чтобы сохранить её. |
{ "type": "warning", "code": "idle_timeout_soon" }У error есть code и message. По живому сокету могут прийти такие коды:
code | Фатальная | Значение |
|---|---|---|
invalid_message | Нет | Текстовый кадр, который не является finalize или end. |
invalid_token | Да | Токен неверен, истёк или уже использован. Сокет закрывается с 4001. |
not_found | Да | Сессии не существует. Сокет закрывается с 4004. |
session_ended | Да | Сессия уже завершена. Сокет закрывается с 4004. |
{ "type": "error", "code": "invalid_message", "message": "Expected {\"type\":\"finalize\"} or {\"type\":\"end\"}" }reconnect приходит до того, как сервер закроет сокет с кодом 1012, например во время деплоя. Получите новый токен и возобновите сессию.
{ "type": "reconnect", "reason": "server_restart" }session.ended — последнее событие каждой сессии. Числа в нём — итоговые:
{
"type": "session.ended",
"reason": "client_ended",
"transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
"minutes_charged": 3,
"connected_ms": 142500,
"audio_ms": 141900
}Объект строки
| Поле | Значение |
|---|---|
id | Число с нуля, которое растёт на единицу с каждой строкой. Это индекс сегмента в сохранённом транскрипте. |
start_ms, end_ms | Начало и конец во времени звука сессии. Это время продолжается после переподключений. |
speaker | Метка говорящего, если включён diarize, иначе null. Метки такие же, как в обычных транскриптах. |
language | Определённый или заданный язык строки либо null, если он ещё неизвестен. |
text | Текст оригинала на данный момент. |
final | true, когда текст оригинала больше не изменится. |
translation | null без translate_to, иначе language, text и final. |
Строки с final: false показывайте более бледным стилем: их текст ещё может измениться. Текст строки с final: true больше не меняется, и финальная строка никогда не удаляется.
В SDK поля строки пишутся в camelCase: startMs, endMs.
Причины завершения
session.ended и GET /v1/live/sessions/:id возвращают end_reason:
| Причина | Значение |
|---|---|
client_ended | Вы отправили end. |
balance_exhausted | Следующее списание не прошло. Сессия завершается на границе минуты и сохраняется. |
idle_timeout | Звука не было 5 минут. |
max_duration | Сессия была подключена 300 минут. Начните новую. |
disconnected | Клиент отсутствовал больше 2 минут и не возобновил сессию. |
upstream_error | Сбой у поставщика распознавания речи. |
internal_error | Ошибка на нашей стороне. |
not_connected | К сессии не подключились за 5 минут. За неё ничего не списывается, а увидеть её можно только через GET. |
Коды закрытия
| Код | Значение |
|---|---|
1000 | Обычное закрытие. Всегда следует за session.ended. |
1011 | Неожиданная ошибка сервера. Сервер восстанавливает сессию: возобновите её с новым токеном. |
1012 | Перезапуск сервера. Идёт после reconnect: возобновите сессию с новым токеном. |
4001 | Токен неверен, истёк или уже использован. |
4004 | Сессия не найдена или уже завершена. |
4008 | Нарушение протокола: бинарный кадр нечётной длины или слишком большого размера. |
4010 | Заменено более новым подключением к той же сессии. |
Пока API перезапускается, на новый запрос WebSocket может прийти HTTP 503. Повторите попытку чуть позже.
Возобновление и перехват
Сессия переживает обрыв сети, деплой API и сбой. После обрыва она 2 минуты ждёт вас, а затем завершается с причиной disconnected и сохраняется.
- Вызовите
POST /v1/live/sessions/:id/token. В ответе будутtoken,token_expires_atиwebsocket_url, те же поля, что и при создании сессии. Когда сессия уже завершена, вызов вернёт409 session_ended. - Откройте новый WebSocket с этим токеном.
- Придёт
session.startedсresumed: true.offset_msпоказывает, сколько звука сессия уже обработала: продолжайте с этого места. Идентификаторы строк продолжают нумерацию.
Строки, которые уже были финальными, сохраняются. Незавершённый хвост на момент обрыва теряется, поэтому, если вы буферизуете звук, отправьте заново несколько секунд с offset_ms. Время переподключения не оплачивается.
Перехват — это тот же вызов. Если сервер ещё считает сокет открытым, новое корректное подключение заменяет его, а старый сокет закрывается с 4010 после сохранения его строк. Лимит одновременных сессий возобновлению не мешает.
SDK делает всё это за вас: при обрыве или событии reconnect он получает токен и переподключается с задержкой от 0.5 до 8 секунд в течение 2 минут, а затем повторно отправляет последние 10 секунд звука, отправленного за время обрыва.
Оплата
Live списывает минуты с того же баланса, что и обычные транскрибации.
- Оплачивается время подключения с момента принятия сокета до его закрытия. Тишина и видео на паузе тоже считаются. Время между обрывом и возобновлением не считается.
- За каждую начатую минуту, минимум одна. Первая минута списывается при первом принятом подключении, каждая следующая — когда начинается.
- Быстрее реального времени оплачивается как длительность звука. Если отправленный звук длиннее времени подключения, оплачивается длительность звука.
- Тариф берите из API. Начатая минута стоит
rateминут баланса с округлением вверх. Обычно значение равно 1, но может быть дробным. Текущее значение читайте изrates.liveвGET /v1/account, изrateвGET /v1/live/capabilitiesи изrateсозданной сессии. После создания сессии её значение не меняется. - Перевод бесплатный.
- Без подключения нет списания. Сессия, к которой не подключились за 5 минут, завершается как
not_connected. - При сбоях деньги возвращаются. Если сессия завершилась с
upstream_errorилиinternal_error, не выдав ни одной строки, все списания возвращаются. Пустая сессия из-за тишины оплачивается и сохраняется пустой. - Когда баланс заканчивается и включено автопополнение, transcript1 списывает оплату с сохранённой карты, пока сессия продолжает стримить, и сессия продолжается. Если автопополнение выключено или приостановлено либо списание не прошло, сессия завершается, сохраняется и закрывается с
balance_exhaustedна границе минуты. Сначала приходит предупреждениеbalance_low, если только не включено автопополнение.
Если доступного баланса меньше одной начатой минуты по текущему тарифу, создание сессии вернёт 402 insufficient_minutes. Если включено автопополнение, transcript1 сначала пробует пополнить баланс. См. «Тарифы и лимиты».
Лимиты
| Free | Monthly | Annual | |
|---|---|---|---|
| Одновременных live-сессий | 1 | 2 | 3 |
- Сессия учитывается, пока она в статусе
waiting,connectedилиdisconnected(в пределах двухминутного окна). При превышении лимитаPOST /v1/live/sessionsвозвращает429 too_many_live_sessionsсRetry-After: 30. - Live-сессии не входят в лимит активных задач.
- Сессия длится не больше 300 минут времени подключения, после чего завершается с
max_duration. - Сессия без звука 5 минут завершается с
idle_timeout; предупреждениеidle_timeout_soonприходит через 4 минуты. Поток на паузе по-прежнему оплачивается как время подключения и считается простоем, поэтому при долгой паузе завершайте сессию. - transcript1 ограничивает все live-сессии в сумме. Когда ёмкость исчерпана,
POST /v1/live/sessionsвозвращает503 live_capacity— такую ошибку можно повторить. ПодождитеRetry-After(30 секунд) и повторите запрос. Проверка выполняется только при создании сессии, но не посреди неё. - Создание сессий и получение токенов входят в ваш лимит запросов в минуту.
- Звук принимается только в виде сырого PCM:
pcm_s16le, моно, от 8000 до 48000 Гц.
Возможности
GET /v1/live/capabilities описывает, что live поддерживает прямо сейчас, чтобы клиентам не приходилось зашивать правила поставщика. Нужное разрешение — 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
}Здесь списки сокращены. Полные читайте из API.
translation.pairsравен"any"(любой определённый язык в любой целевой) или списку объектов сfromиto.translation.requires_languageравенtrue, если для перевода нужен явныйlanguageвместоauto.- Когда live не настроен, ответ содержит
available: falseи толькоrate, а создание сессии возвращает503 live_unavailable.
В SDK: await t1.live.capabilities().
Чтение сессии
GET /v1/live/sessions/:id возвращает состояние сессии. Нужное разрешение — 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 или ended. end_reason равен null, пока сессия не завершена. В SDK: await t1.live.get(id).
Ошибки
Ошибки запросов приходят в обычном формате ошибки. Вот те, что возвращают POST /v1/live/sessions и соседние эндпоинты:
| HTTP-статус | code | Можно повторить | Когда |
|---|---|---|---|
| 422 | invalid_request | Нет | Поле пропущено или неверно, например sample_rate вне диапазона от 8000 до 48000. |
| 422 | unsupported_language | Нет | language не поддерживается в live. |
| 422 | unsupported_translation | Нет | Язык перевода или пара языков не поддерживается. |
| 402 | insufficient_minutes | Нет | Доступный баланс меньше одной начатой минуты по текущему тарифу. |
| 403 | email_not_verified | Нет | Сначала подтвердите почту. |
| 429 | too_many_live_sessions | Да | Заняты все одновременные live-сессии вашего тарифа. Подождите Retry-After. |
| 503 | live_capacity | Да | Вся ёмкость live занята. Подождите Retry-After (30 секунд). |
| 503 | live_unavailable | Нет | Расшифровка в реальном времени сейчас недоступна. |
| 409 | session_ended | Нет | POST /v1/live/sessions/:id/token для завершённой сессии. |
Проблемы на сокете приходят как события error и коды закрытия.
Сохранённые транскрипты
При save: true (по умолчанию) после завершения сессия сохраняется как обычная транскрибация. Сам звук не хранится никогда.
transcription_idвозвращается при создании сессии. Пока сессия идёт, у неё статусlive, после завершения —completed. Если к сессии так и не подключились, остаётся транскрибация со статусомcanceled.- У транскрибации есть
source: { "platform": "live", "session_id": "lv_…" }. Ссылки и файла нет. В личном кабинете она отображается как live-сессия. - Каждая строка становится одним сегментом с тем же индексом, что и
idстроки. Транскрипт можно читать как JSON, текст, SRT или VTT, как любую другую транскрибацию: см. справочник API. - Перевод, сделанный в реальном времени, сохраняется вместе с ней. Читайте его с параметром
translation, как описано в разделе «Перевод». - Когда транскрибация получит статус
completed, другие языки перевода можно добавить позже черезPOST /v1/transcriptions/:id/translations. См. «Перевод».
При save: false после завершения сессии ничего не хранится: транскрибации нет, а transcription_id равен null. Все строки вы по-прежнему получаете по сокету, и списание остаётся обычным.
Безопасность
- Токен, который вы получаете при создании сессии, одноразовый и действует 60 секунд. Он передаётся как субпротокол WebSocket, а не в URL, поэтому не попадает в журналы доступа. Утёкший токен бесполезен после использования или истечения срока.
- Используйте API-ключ только на сервере, который вы контролируете. Никогда не вставляйте API-ключ в веб-страницу или расширение.
- В браузере или расширении входите через OAuth и передавайте OAuth-токен доступа в
getAccessTokenметодаcreateLiveClient. Для запуска сессий токену нужно разрешениеtranscriptions:write. OAuth-токены доступа истекают через 1 час, и при каждом возобновленииgetAccessTokenзапрашивается заново. - Сессию может создать и ваш сервер с API-ключом, передав браузеру только
websocket_urlиtoken. Тогда клиент подключается напрямую. Без нового токена от вас он не сможет возобновить сессию.
Дата обновления 3 ноября 2026 г.