DokuLive-Transkription

Live-Transkription

Streame Mikrofon- oder Tab-Audio per WebSocket an transcript1 und erhalte Untertitelzeilen, auf Wunsch mit Übersetzung in Echtzeit, noch während gesprochen wird.

Die Live-Transkription verwandelt einen Audiostream in Untertitelzeilen, während noch gesprochen wird. Dein Client sendet rohes PCM-Audio über einen WebSocket, und transcript1 schickt Zeilen zurück, die zunächst als vorläufige Version erscheinen und dann endgültig werden. Mit translate_to enthält jede Zeile zusätzlich ihre Übersetzung. Wenn die Sitzung endet, wird das Ergebnis als normale Transkription gespeichert.

Für Live gibt es keine eigenen Tarife: Es nutzt dasselbe Minutenguthaben wie alles andere. Wie die Verbindungszeit gezählt wird, steht unter Abrechnung.

Wann Live sinnvoll ist

Nimm Live, wennNimm eine normale Transkription, wenn
das Audio gerade erst entsteht: ein Anruf, ein Vortrag, ein Stream, ein Video, das du ansiehst.du einen Link oder eine fertige Datei hast.
du Untertitel ein bis zwei Sekunden nach dem Gesprochenen brauchst.du die höchste Genauigkeit willst und auf das Ergebnis warten kannst.
du neben jeder Zeile eine Übersetzung in Echtzeit willst.du ein fertiges Transkript übersetzen willst, auch in mehrere Sprachen.

Live nimmt nur Audio an, und zwar als rohes PCM. Videolinks, Uploads, WebM, Opus und MP3 werden nicht akzeptiert. Nimm das Audio in deinem Client auf und wandle es vorher in PCM um. Audio im Browser aufnehmen zeigt, wie.

Schnellstart mit dem SDK

Installiere SDK 1.5.0 oder neuer: npm i @transcript1/sdk@latest. t1.live.start legt die Sitzung an, verbindet sich und wird aufgelöst, sobald die Sitzung gestartet ist. Du kannst also sofort Audio senden.

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

Zeilen werden an Ort und Stelle aktualisiert. Ersetze eine vorhandene Zeile, sobald eine mit derselben id eintrifft, oder lies alle über live.lines. end() beendet die Sitzung, wartet auf die letzten Zeilen und speichert das Transkript. Alle Methoden und Events stehen unter Node.js-SDK.

Schnellstart mit einem rohen WebSocket

Ohne das SDK sind es zwei Schritte: eine Sitzung per HTTPS anlegen und dann den WebSocket mit dem Einmal-Token öffnen, das du zurückbekommen hast.

Sitzung anlegen

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

Die API antwortet mit 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 ist null, wenn save auf false steht. Das Token gilt nur einmal und läuft nach 60 Sekunden ab, verbinde dich also sofort. rate ist die Zahl der Guthabenminuten, die pro angefangener Minute dieser Sitzung abgebucht werden; siehe Abrechnung.

Der Request-Body kennt diese Felder. Alle sind optional, der nötige Scope ist transcriptions:write:

FeldStandardRegel
languageautoauto oder ein zweistelliger Code, den die Live-Transkription unterstützt.
translate_tokeinerEin zweistelliger Code. Pro Sitzung eine Zielsprache. Siehe Live übersetzen.
diarizefalseSprecher kennzeichnen. Manche Konfigurationen können das nicht, etwa zusammen mit language: "auto", und antworten mit 422. Prüfe die Fähigkeiten.
audio_formatpcm_s16leDer einzige Wert. Das Feld gibt es, damit später weitere Formate dazukommen können.
sample_rate16000Eine ganze Zahl von 8000 bis 48000. Nur Mono.
savetrueMit false bleibt nach dem Ende der Sitzung nichts erhalten. Siehe Gespeicherte Transkripte.

Verbinden und streamen

Öffne den WebSocket mit dem Token als zweitem Subprotokoll. Das funktioniert in Browsern und unter Node 22+ ohne eigene Header. Warte auf session.started, bevor du Audio sendest: Binärframes, die früher ankommen, werden verworfen, und du erhältst eine Warnung 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));

Unter Node 20 gibt es keinen eingebauten WebSocket: Nimm das Paket ws, das dieselbe Form new WebSocket(url, protocols) hat.

Auf ein gültiges Token antwortet der Server mit dem Subprotokoll t1-live und sonst nichts. Ein ungültiges Token lässt den Handshake nie scheitern: Der Socket öffnet sich, du erhältst ein error-Event und der Socket schließt mit einem 4xxx-Code. So zeigt ein Browser den echten Grund statt eines allgemeinen 1006. Siehe Close-Codes.

Audio im Browser aufnehmen

Live-Audio muss 16-Bit-PCM mit Vorzeichen, Little-Endian, Mono, mit der sample_rate der Sitzung sein. Browser liefern Gleitkomma-Samples, wandle sie also in einem AudioWorklet um. Dieses Beispiel nimmt das Mikrofon als 16-kHz-PCM in 100-ms-Blöcken auf und sendet es über den Browser-Einstiegspunkt des SDK. getAccessToken gibt ein OAuth-Access-Token zurück; lege nie einen API-Schlüssel in einem Browser ab. Siehe Sicherheit.

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

Das Worklet ist für 16 kHz geschrieben. Läuft der Kontext mit einer anderen Rate, sind die Blöcke nicht mehr 100 ms lang, das PCM bleibt aber korrekt, weil sampleRate zum Kontext passt. Um statt des Mikrofons den Ton eines Tabs oder Videos aufzunehmen, erstelle den MediaStream mit getDisplayMedia oder aus dem captureStream() eines Medienelements und verbinde ihn genauso.

Live übersetzen

Füge der Sitzung translate_to hinzu, und transcript1 übersetzt jede Zeile, sobald sie gesprochen wird.

  • Pro Sitzung eine Zielsprache. Weitere Sprachen fügst du danach dem gespeicherten Transkript über den Übersetzungs-Endpoint hinzu.
  • Jede line hat ein Original (text, final) und ein translation-Objekt (language, text, final). translation ist null, wenn translate_to nicht gesetzt ist.
  • Original und Übersetzung werden unabhängig voneinander endgültig: Eine Zeile kann ein endgültiges Original haben, während ihre Übersetzung sich noch ändert. Zeige eine Zeile erst als endgültig, wenn beides endgültig ist.
  • Eine Zeile, die schon in der Zielsprache ist, nutzt ihren eigenen Text als Übersetzung und ist endgültig, sobald die Zeile abgeschlossen wird.
  • Eine Zeile wird abgeschlossen, wenn der Sprecher pausiert, der Sprecher wechselt, eine Pause zwischen Wörtern länger als 0.9 Sekunden dauert, die Zeile zwei Untertitelreihen (84 Zeichen) oder 15 Sekunden erreicht oder du finalize sendest. Bei aktiver Übersetzung kann das Abschließen einer Zeile etwa 3 Sekunden länger dauern, bis ihre Übersetzung da ist. Dann kann die Übersetzung offen bleiben (final: false) und weiterwachsen, obwohl das Original schon endgültig ist.
  • Die Übersetzung ist kostenlos. Sie verbraucht keine Minuten.

Welche Zielsprachen und Sprachpaare verfügbar sind, hängt vom Live-Spracherkennungsanbieter ab. Lies die Fähigkeiten, statt Regeln fest einzubauen. Eine nicht unterstützte Zielsprache liefert 422 unsupported_translation. Ein Anbieter kann zum Beispiel eine ausdrückliche language statt auto verlangen oder nur Paare mit Englisch erlauben.

WebSocket-Protokoll

Verbinden

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

Das Token wird als WebSocket-Subprotokoll übertragen. Es erscheint nie in einer URL oder einem Zugriffslog. Sende keinen Authorization-Header: Das Token ist die Zugangsberechtigung. Der Server sendet alle 30 Sekunden einen Ping; Browser und das Paket ws antworten automatisch. Ein Socket, der 60 Sekunden lang kein Pong sendet, wird getrennt.

Eine gültige Verbindung zu einer Sitzung, die schon einen Socket hat, ersetzt den alten. Siehe Fortsetzen und Übernehmen.

Nachrichten des Clients

NachrichtBedeutung
BinärframeAudio: PCM s16le Mono mit der sample_rate der Sitzung. Jeder Frame hat eine gerade Länge und höchstens 256 KB. Etwa 100 ms pro Frame sind eine gute Größe.
{"type":"finalize"}Schließt die aktuelle Zeile sofort ab, zum Beispiel wenn das Video pausiert wird.
{"type":"end"}Beendet die Sitzung und speichert sie. Der Server antwortet mit session.ended und schließt den Socket.
Alles andereEin nicht fatales error-Event mit dem Code invalid_message.

Ein Frame mit ungerader Länge oder zu groß ist ein Protokollverstoß und schließt den Socket mit 4008.

Beginne erst nach session.started mit dem Senden. Wer Audio schneller als in Echtzeit sendet, bezahlt die Audiozeit statt der Verbindungszeit (siehe Abrechnung). Sende also in dem Tempo, in dem das Audio entsteht.

Events des Servers

Jedes Event ist ein JSON-Textframe mit einem type.

EventWird gesendet, wenn
session.startedDie Verbindung akzeptiert wurde. Ab jetzt Audio senden.
lineEine Untertitelzeile entstanden ist oder sich geändert hat.
usageNach jeder Abbuchung.
warningEtwas Aufmerksamkeit braucht, die Sitzung aber weiterläuft.
errorEin nicht fataler Fehler oder der Grund für ein sofortiges Schließen.
reconnectDer Server gleich neu startet. Setze die Sitzung fort.
session.endedDie Sitzung vorbei und gespeichert ist. Das letzte Event vor einem normalen Schließen.

session.started sagt, ob es sich um ein Fortsetzen handelt und wie viel Audio die Sitzung schon verarbeitet hat:

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

line enthält den vollständigen aktuellen Zustand einer Zeile. Ersetze also die Zeile, die du mit derselben id hast. Siehe Das Zeilenobjekt.

{
  "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 kommt nach jeder Abbuchung, mit der Verbindungszeit, der Audiozeit, den bisher abgebuchten Guthabenminuten und den Minuten, die dir bleiben:

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

warning hat einen code:

codeBedeutung
balance_lowDie nächste Abbuchung würde scheitern. Die Sitzung endet mit balance_exhausted, sobald sie fällig ist. Wird nicht gesendet, solange die automatische Aufladung aktiv ist.
audio_droppedAudio kam an, bevor die Sitzung bereit war, oder zu viel Audio, während sich der Spracherkennungsanbieter verband. Das älteste Audio wurde verworfen.
idle_timeout_soonSeit 4 Minuten kein Audio. Die Sitzung endet nach 5 Minuten ohne Audio. Sende Audio, um sie zu behalten.
{ "type": "warning", "code": "idle_timeout_soon" }

error hat einen code und eine message. Diese Codes können auf einem Live-Socket ankommen:

codeFatalBedeutung
invalid_messageNeinEin Textframe, der weder finalize noch end ist.
invalid_tokenJaDas Token ist ungültig, abgelaufen oder schon verwendet. Der Socket schließt mit 4001.
not_foundJaDie Sitzung existiert nicht. Der Socket schließt mit 4004.
session_endedJaDie Sitzung ist schon beendet. Der Socket schließt mit 4004.
{ "type": "error", "code": "invalid_message", "message": "Expected {\"type\":\"finalize\"} or {\"type\":\"end\"}" }

reconnect kommt, bevor der Server den Socket mit 1012 schließt, zum Beispiel bei einem Deployment. Hole ein neues Token und setze die Sitzung fort.

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

session.ended ist das letzte Event jeder Sitzung. Die Zahlen sind die Endsummen:

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

Das Zeilenobjekt

FeldBedeutung
idEine bei 0 beginnende Zahl, die mit jeder Zeile um eins wächst. Sie ist der Segmentindex im gespeicherten Transkript.
start_ms, end_msBeginn und Ende in der Audiozeit der Sitzung. Diese Zeit läuft über Wiederverbindungen hinweg weiter.
speakerDas Sprecherlabel, wenn diarize aktiv ist, sonst null. Die Labels sind dieselben wie in normalen Transkripten.
languageDie erkannte oder angegebene Sprache der Zeile, oder null, solange sie noch nicht feststeht.
textDer bisherige Originaltext.
finaltrue, sobald sich der Originaltext nicht mehr ändert.
translationnull ohne translate_to, sonst language, text und final.

Zeige Zeilen mit final: false in einem helleren Stil: Ihr Text kann sich noch ändern. Der Text einer Zeile mit final: true ändert sich nie wieder, und eine endgültige Zeile wird nie entfernt.

Das SDK nutzt für Zeilenfelder camelCase: startMs, endMs.

Endgründe

session.ended und GET /v1/live/sessions/:id liefern einen end_reason:

GrundBedeutung
client_endedDu hast end gesendet.
balance_exhaustedDie nächste Abbuchung ist gescheitert. Die Sitzung endet an einer Minutengrenze und wird gespeichert.
idle_timeout5 Minuten ohne Audio.
max_durationDie Sitzung war 300 Minuten verbunden. Starte eine neue.
disconnectedDer Client war länger als 2 Minuten weg, ohne die Sitzung fortzusetzen.
upstream_errorDer Spracherkennungsanbieter ist ausgefallen.
internal_errorBei uns ist ein Fehler aufgetreten.
not_connectedDie Sitzung wurde innerhalb von 5 Minuten nie verbunden. Sie wird nie berechnet und ist nur über GET sichtbar.

Close-Codes

CodeBedeutung
1000Normales Schließen. Folgt immer auf session.ended.
1011Unerwarteter Serverfehler. Der Server stellt die Sitzung wieder her: Setze sie mit einem neuen Token fort.
1012Server-Neustart. Folgt auf reconnect: Setze die Sitzung mit einem neuen Token fort.
4001Das Token ist ungültig, abgelaufen oder schon verwendet.
4004Die Sitzung wurde nicht gefunden oder ist schon beendet.
4008Protokollverstoß: ein Binärframe mit ungerader Länge oder zu groß.
4010Durch eine neuere Verbindung zur selben Sitzung ersetzt.

Während die API neu startet, kann eine neue WebSocket-Anfrage mit HTTP 503 beantwortet werden. Versuche es kurz danach erneut.

Fortsetzen und Übernehmen

Eine Sitzung übersteht einen Netzwerkabbruch, ein API-Deployment und einen Absturz. Nach einem Abbruch wartet sie 2 Minuten auf dich; danach endet sie als disconnected und wird gespeichert.

  1. Rufe POST /v1/live/sessions/:id/token auf. Die Antwort enthält token, token_expires_at und websocket_url, dieselben Felder wie beim Anlegen der Sitzung. Der Aufruf scheitert mit 409 session_ended, sobald die Sitzung beendet ist.
  2. Öffne einen neuen WebSocket mit diesem Token.
  3. session.started kommt mit resumed: true. offset_ms ist die Menge an Audio, die die Sitzung schon verarbeitet hat: Mach mit dem Audio ab dieser Stelle weiter. Die Zeilen-IDs laufen weiter.

Zeilen, die schon endgültig waren, bleiben erhalten. Der laufende Rest zum Zeitpunkt des Abbruchs geht verloren. Sende daher ein paar Sekunden Audio ab offset_ms erneut, wenn du es zwischenspeicherst. Lücken beim Wiederverbinden werden nicht berechnet.

Das Übernehmen ist derselbe Aufruf. Hält der Server noch einen Socket für offen, ersetzt eine neue gültige Verbindung ihn, und der alte Socket wird mit 4010 geschlossen, nachdem seine Zeilen gespeichert sind. Das Limit für gleichzeitige Sitzungen blockiert ein Fortsetzen nie.

Das SDK erledigt das alles für dich: Bei einem Abbruch oder einem reconnect-Event holt es ein Token und verbindet sich mit einem Backoff von 0.5 bis 8 Sekunden neu, höchstens 2 Minuten lang, und sendet die letzten 10 Sekunden des Audios, das während der Lücke gesendet wurde, noch einmal.

Abrechnung

Die Live-Transkription nutzt dasselbe Minutenguthaben wie Batch-Transkriptionen.

  • Abgerechnet wird die Verbindungszeit, von dem Moment, in dem der Socket akzeptiert wird, bis er geschlossen wird. Stille und ein pausiertes Video zählen. Die Zeit zwischen einem Abbruch und dem Fortsetzen nicht.
  • Pro angefangener Minute, mindestens 1. Die erste Minute wird abgebucht, wenn die erste Verbindung akzeptiert wird, jede weitere, sobald sie beginnt.
  • Schneller als Echtzeit wird als Audiozeit abgerechnet. Ist das gesendete Audio länger als die Verbindungszeit, wird stattdessen die Audiozeit berechnet.
  • Die Rate liest du aus der API. Eine angefangene Minute kostet rate Guthabenminuten, aufgerundet. Die Rate ist meist 1 und kann Dezimalstellen haben. Die Live-Rate steht in rates.live von GET /v1/account, in rate von GET /v1/live/capabilities und in rate der Sitzung, die du anlegst. Die Rate einer Sitzung steht nach dem Anlegen fest.
  • Die Übersetzung ist kostenlos.
  • Nie verbunden heißt nicht berechnet. Eine Sitzung, die innerhalb von 5 Minuten nicht verbunden wird, endet als not_connected.
  • Fehler werden erstattet. Endet eine Sitzung mit upstream_error oder internal_error, bevor sie auch nur eine Zeile erzeugt hat, wird alles zurückerstattet. Eine durch Stille leere Sitzung wird berechnet und leer gespeichert.
  • Ist das Guthaben aufgebraucht und die automatische Aufladung aktiv, belastet transcript1 deine hinterlegte Karte, während die Sitzung weiter streamt, und die Sitzung läuft weiter. Ist die automatische Aufladung aus oder pausiert oder scheitert die Abbuchung, wird die Sitzung abgeschlossen, gespeichert und endet an einer Minutengrenze mit balance_exhausted. Vorher kommt die Warnung balance_low, außer die automatische Aufladung ist aktiv.

Das Anlegen einer Sitzung liefert 402 insufficient_minutes, wenn dein nutzbares Guthaben unter einer angefangenen Minute zur aktuellen Rate liegt. Ist die automatische Aufladung aktiv, versucht transcript1 zuerst aufzuladen. Siehe Abrechnung und Limits.

Limits

FreeMonthlyAnnual
Gleichzeitige Live-Sitzungen123
  • Eine Sitzung zählt, solange sie waiting, connected oder disconnected (innerhalb ihres 2-Minuten-Fensters) ist. Über dem Limit antwortet POST /v1/live/sessions mit 429 too_many_live_sessions und Retry-After: 30.
  • Live-Sitzungen zählen nicht zu deinem Limit für aktive Jobs.
  • Eine Sitzung dauert höchstens 300 Minuten Verbindungszeit. Danach endet sie mit max_duration.
  • Eine Sitzung ohne Audio endet nach 5 Minuten mit idle_timeout; nach 4 Minuten erhältst du idle_timeout_soon. Ein pausierter Stream berechnet weiter Verbindungszeit und gilt als untätig, beende die Sitzung also bei einer langen Pause.
  • transcript1 begrenzt alle Live-Sitzungen zusammen. Sind alle belegt, antwortet POST /v1/live/sessions mit 503 live_capacity, was sich wiederholen lässt. Warte Retry-After (30 Sekunden) ab und versuche es erneut. Das wird nur beim Anlegen einer Sitzung geprüft, nie mittendrin.
  • Das Anlegen von Sitzungen und das Abrufen von Tokens zählen zu deinen Requests pro Minute.
  • Audio ist nur rohes PCM: pcm_s16le, Mono, 8000 bis 48000 Hz.

Fähigkeiten

GET /v1/live/capabilities beschreibt, was die Live-Transkription gerade unterstützt, damit Clients keine Anbieterregeln fest einbauen müssen. Der nötige Scope ist 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
}

Die Listen sind hier gekürzt. Lies sie aus der API.

  • translation.pairs ist "any" (jede erkannte Sprache in jede Zielsprache) oder eine Liste von Objekten mit from und to.
  • translation.requires_language ist true, wenn die Übersetzung eine ausdrückliche language statt auto braucht.
  • Ist die Live-Transkription nicht konfiguriert, lautet die Antwort available: false und enthält nur rate; das Anlegen einer Sitzung liefert dann 503 live_unavailable.

Mit dem SDK: await t1.live.capabilities().

Sitzung abrufen

GET /v1/live/sessions/:id liefert den Zustand einer Sitzung. Der nötige Scope ist 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 ist waiting, connected, disconnected oder ended. end_reason ist null, bis die Sitzung beendet ist. Mit dem SDK: await t1.live.get(id).

Fehler

Request-Fehler verwenden die übliche Fehlerstruktur. Diese liefern POST /v1/live/sessions und die zugehörigen Endpoints:

HTTP-StatuscodeErneut versuchen?Wann
422invalid_requestNeinEin Feld fehlt oder ist ungültig, zum Beispiel eine sample_rate außerhalb von 8000 bis 48000.
422unsupported_languageNeinlanguage wird für die Live-Transkription nicht unterstützt.
422unsupported_translationNeinDie Zielsprache oder das Sprachpaar wird nicht unterstützt.
402insufficient_minutesNeinDein nutzbares Guthaben liegt unter einer angefangenen Minute zur aktuellen Rate.
403email_not_verifiedNeinBestätige zuerst deine E-Mail-Adresse.
429too_many_live_sessionsJaDie gleichzeitigen Live-Sitzungen deines Tarifs sind ausgeschöpft. Warte Retry-After ab.
503live_capacityJaDie gesamte Live-Kapazität ist belegt. Warte Retry-After (30 Sekunden) ab.
503live_unavailableNeinDie Live-Transkription ist gerade nicht verfügbar.
409session_endedNeinPOST /v1/live/sessions/:id/token für eine bereits beendete Sitzung.

Probleme auf dem Socket kommen als error-Events und Close-Codes.

Gespeicherte Transkripte

Mit save: true (Standard) wird die Sitzung beim Beenden als normale Transkription gespeichert. Das Audio wird nie gespeichert.

  • transcription_id bekommst du beim Anlegen der Sitzung. Der Status ist live, solange die Sitzung läuft, und completed, wenn sie beendet ist. Eine Sitzung, die nie verbunden war, hinterlässt eine Transkription mit dem Status canceled.
  • Die Transkription hat source: { "platform": "live", "session_id": "lv_…" }. Es gibt weder Link noch Datei. Das Dashboard listet sie als Live-Sitzung.
  • Jede Zeile wird zu einem Segment mit demselben Index wie die id der Zeile. Lies das Transkript wie jede andere Transkription als JSON, Text, SRT oder VTT: siehe API-Referenz.
  • Die Live-Übersetzung wird mitgespeichert. Lies sie mit dem Parameter translation, wie unter Übersetzung beschrieben.
  • Weitere Übersetzungssprachen fügst du später mit POST /v1/transcriptions/:id/translations hinzu, sobald die Transkription completed ist. Siehe Übersetzung.

Mit save: false bleibt nach dem Ende der Sitzung nichts erhalten: Es gibt keine Transkription, und transcription_id ist null. Du bekommst trotzdem jede Zeile über den Socket, und die übliche Abrechnung gilt.

Sicherheit

  • Das Token, das du beim Anlegen einer Sitzung erhältst, gilt nur einmal und läuft nach 60 Sekunden ab. Es wird als WebSocket-Subprotokoll gesendet, nicht in der URL, und bleibt so aus Zugriffslogs heraus. Ein durchgesickertes Token ist nutzlos, sobald es verwendet wurde oder abgelaufen ist.
  • Verwende einen API-Schlüssel nur auf einem Server, den du kontrollierst. Lege einen API-Schlüssel nie in eine Webseite oder Erweiterung.
  • Melde Nutzer in einem Browser oder einer Erweiterung per OAuth an und gib das OAuth-Access-Token an getAccessToken von createLiveClient. Zum Starten von Sitzungen braucht das Token den Scope transcriptions:write. OAuth-Access-Tokens laufen nach 1 Stunde ab, und jedes Fortsetzen fragt getAccessToken nach einem frischen.
  • Dein Server kann die Sitzung auch mit seinem API-Schlüssel anlegen und dem Browser nur websocket_url und token übergeben. Der Client verbindet sich dann direkt. Ohne ein neues Token von dir kann er die Sitzung aber nicht fortsetzen.

Zuletzt aktualisiert 3. November 2026