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, wenn | Nimm 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:
| Feld | Standard | Regel |
|---|---|---|
language | auto | auto oder ein zweistelliger Code, den die Live-Transkription unterstützt. |
translate_to | keiner | Ein zweistelliger Code. Pro Sitzung eine Zielsprache. Siehe Live übersetzen. |
diarize | false | Sprecher kennzeichnen. Manche Konfigurationen können das nicht, etwa zusammen mit language: "auto", und antworten mit 422. Prüfe die Fähigkeiten. |
audio_format | pcm_s16le | Der einzige Wert. Das Feld gibt es, damit später weitere Formate dazukommen können. |
sample_rate | 16000 | Eine ganze Zahl von 8000 bis 48000. Nur Mono. |
save | true | Mit 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
linehat ein Original (text,final) und eintranslation-Objekt (language,text,final).translationistnull, wenntranslate_tonicht 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
finalizesendest. 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
| Nachricht | Bedeutung |
|---|---|
| Binärframe | Audio: 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 andere | Ein 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.
| Event | Wird gesendet, wenn |
|---|---|
session.started | Die Verbindung akzeptiert wurde. Ab jetzt Audio senden. |
line | Eine Untertitelzeile entstanden ist oder sich geändert hat. |
usage | Nach jeder Abbuchung. |
warning | Etwas Aufmerksamkeit braucht, die Sitzung aber weiterläuft. |
error | Ein nicht fataler Fehler oder der Grund für ein sofortiges Schließen. |
reconnect | Der Server gleich neu startet. Setze die Sitzung fort. |
session.ended | Die 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:
code | Bedeutung |
|---|---|
balance_low | Die 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_dropped | Audio kam an, bevor die Sitzung bereit war, oder zu viel Audio, während sich der Spracherkennungsanbieter verband. Das älteste Audio wurde verworfen. |
idle_timeout_soon | Seit 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:
code | Fatal | Bedeutung |
|---|---|---|
invalid_message | Nein | Ein Textframe, der weder finalize noch end ist. |
invalid_token | Ja | Das Token ist ungültig, abgelaufen oder schon verwendet. Der Socket schließt mit 4001. |
not_found | Ja | Die Sitzung existiert nicht. Der Socket schließt mit 4004. |
session_ended | Ja | Die 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
| Feld | Bedeutung |
|---|---|
id | Eine bei 0 beginnende Zahl, die mit jeder Zeile um eins wächst. Sie ist der Segmentindex im gespeicherten Transkript. |
start_ms, end_ms | Beginn und Ende in der Audiozeit der Sitzung. Diese Zeit läuft über Wiederverbindungen hinweg weiter. |
speaker | Das Sprecherlabel, wenn diarize aktiv ist, sonst null. Die Labels sind dieselben wie in normalen Transkripten. |
language | Die erkannte oder angegebene Sprache der Zeile, oder null, solange sie noch nicht feststeht. |
text | Der bisherige Originaltext. |
final | true, sobald sich der Originaltext nicht mehr ändert. |
translation | null 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:
| Grund | Bedeutung |
|---|---|
client_ended | Du hast end gesendet. |
balance_exhausted | Die nächste Abbuchung ist gescheitert. Die Sitzung endet an einer Minutengrenze und wird gespeichert. |
idle_timeout | 5 Minuten ohne Audio. |
max_duration | Die Sitzung war 300 Minuten verbunden. Starte eine neue. |
disconnected | Der Client war länger als 2 Minuten weg, ohne die Sitzung fortzusetzen. |
upstream_error | Der Spracherkennungsanbieter ist ausgefallen. |
internal_error | Bei uns ist ein Fehler aufgetreten. |
not_connected | Die Sitzung wurde innerhalb von 5 Minuten nie verbunden. Sie wird nie berechnet und ist nur über GET sichtbar. |
Close-Codes
| Code | Bedeutung |
|---|---|
1000 | Normales Schließen. Folgt immer auf session.ended. |
1011 | Unerwarteter Serverfehler. Der Server stellt die Sitzung wieder her: Setze sie mit einem neuen Token fort. |
1012 | Server-Neustart. Folgt auf reconnect: Setze die Sitzung mit einem neuen Token fort. |
4001 | Das Token ist ungültig, abgelaufen oder schon verwendet. |
4004 | Die Sitzung wurde nicht gefunden oder ist schon beendet. |
4008 | Protokollverstoß: ein Binärframe mit ungerader Länge oder zu groß. |
4010 | Durch 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.
- Rufe
POST /v1/live/sessions/:id/tokenauf. Die Antwort enthälttoken,token_expires_atundwebsocket_url, dieselben Felder wie beim Anlegen der Sitzung. Der Aufruf scheitert mit409 session_ended, sobald die Sitzung beendet ist. - Öffne einen neuen WebSocket mit diesem Token.
session.startedkommt mitresumed: true.offset_msist 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
rateGuthabenminuten, aufgerundet. Die Rate ist meist 1 und kann Dezimalstellen haben. Die Live-Rate steht inrates.livevonGET /v1/account, inratevonGET /v1/live/capabilitiesund inrateder 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_erroroderinternal_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 Warnungbalance_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
| Free | Monthly | Annual | |
|---|---|---|---|
| Gleichzeitige Live-Sitzungen | 1 | 2 | 3 |
- Eine Sitzung zählt, solange sie
waiting,connectedoderdisconnected(innerhalb ihres 2-Minuten-Fensters) ist. Über dem Limit antwortetPOST /v1/live/sessionsmit429 too_many_live_sessionsundRetry-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 duidle_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/sessionsmit503 live_capacity, was sich wiederholen lässt. WarteRetry-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.pairsist"any"(jede erkannte Sprache in jede Zielsprache) oder eine Liste von Objekten mitfromundto.translation.requires_languageisttrue, wenn die Übersetzung eine ausdrücklichelanguagestattautobraucht.- Ist die Live-Transkription nicht konfiguriert, lautet die Antwort
available: falseund enthält nurrate; das Anlegen einer Sitzung liefert dann503 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-Status | code | Erneut versuchen? | Wann |
|---|---|---|---|
| 422 | invalid_request | Nein | Ein Feld fehlt oder ist ungültig, zum Beispiel eine sample_rate außerhalb von 8000 bis 48000. |
| 422 | unsupported_language | Nein | language wird für die Live-Transkription nicht unterstützt. |
| 422 | unsupported_translation | Nein | Die Zielsprache oder das Sprachpaar wird nicht unterstützt. |
| 402 | insufficient_minutes | Nein | Dein nutzbares Guthaben liegt unter einer angefangenen Minute zur aktuellen Rate. |
| 403 | email_not_verified | Nein | Bestätige zuerst deine E-Mail-Adresse. |
| 429 | too_many_live_sessions | Ja | Die gleichzeitigen Live-Sitzungen deines Tarifs sind ausgeschöpft. Warte Retry-After ab. |
| 503 | live_capacity | Ja | Die gesamte Live-Kapazität ist belegt. Warte Retry-After (30 Sekunden) ab. |
| 503 | live_unavailable | Nein | Die Live-Transkription ist gerade nicht verfügbar. |
| 409 | session_ended | Nein | POST /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_idbekommst du beim Anlegen der Sitzung. Der Status istlive, solange die Sitzung läuft, undcompleted, wenn sie beendet ist. Eine Sitzung, die nie verbunden war, hinterlässt eine Transkription mit dem Statuscanceled.- 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
idder 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/translationshinzu, sobald die Transkriptioncompletedist. 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
getAccessTokenvoncreateLiveClient. Zum Starten von Sitzungen braucht das Token den Scopetranscriptions:write. OAuth-Access-Tokens laufen nach 1 Stunde ab, und jedes Fortsetzen fragtgetAccessTokennach einem frischen. - Dein Server kann die Sitzung auch mit seinem API-Schlüssel anlegen und dem Browser nur
websocket_urlundtokenü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