DokuREST-API-Referenz
REST-API-Referenz
Parameter und Beispiele für alle /v1-Endpoints. Die OpenAPI-Spezifikation kannst du als JSON oder YAML herunterladen.
Überblick
https://api.transcript1.com/v1Alle API-Aufrufe gehen an die Basis-URL https://api.transcript1.com/v1.
- Sende und empfange JSON. Die Feldnamen verwenden
snake_case. - Zeitstempel folgen ISO 8601 und beziehen sich auf UTC, etwa
2026-10-13T09:30:00.000Z. - Laufzeiten und Zeitangaben im Transkript stehen in Sekunden.
- Für alle Fehler gilt dasselbe Antwortformat. Details findest du unter Fehler.
- Bei authentifizierten Requests enthält jede Antwort die Header
X-RateLimit-LimitundX-RateLimit-Remaining. Mehr dazu unter Tarife und Limits. - Mit
source=autonutzt transcript1 auf YouTube, X, TikTok, Bluesky, Dailymotion und LinkedIn zuerst verwendbare Untertitel in der Originalsprache. Gibt es keine, übernimmt Speech-to-Text. Nicht jedes Video hat Untertitel. Der Modussource=captionslädt kein Audio herunter und wechselt nicht zu STT. Fehlen verwendbare Untertitel, bekommst duno_captions. Mitsource=sttoder aktivierter Sprecherzuordnung wird dagegen direkt das Audio transkribiert. Unter Quellen und Einstellungen siehst du die Einschränkungen je Plattform und erfährst, wann Segment- oder Wort-Zeitstempel verfügbar sind.
Die maschinenlesbare OpenAPI-Spezifikation gibt es in zwei Formaten:
Authentifizierung
curl https://api.transcript1.com/v1/account \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"Authentifiziere deine Requests über den Header Authorization: Authorization: Bearer <token>.
- API-Schlüssel beginnen mit
t1_und funktionieren für alle Endpoints. Erstelle einen im Dashboard unter „API keys“. - OAuth-Access-Tokens beginnen mit
t1o_. MCP-Clients wie Claude und ChatGPT bekommen ein solches Token, wenn du sie verbindest. Es gilt für die Scopestranscriptions:readundtranscriptions:write.
| Scope | Erlaubte Aufrufe |
|---|---|
transcriptions:read | GET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload und GET /v1/account |
transcriptions:write | POST /v1/transcriptions, DELETE /v1/transcriptions/:id und POST /v1/transcriptions/:id/upload/complete |
- Fehlt dem Token der erforderliche Scope, antwortet die API mit
403 insufficient_scope. - Fehlen die Zugangsdaten oder sind sie ungültig, bekommst du
401 unauthorized.
Für PUT /v1/transcriptions/:id/upload/chunks/:index verwendest du stattdessen X-Upload-Token. Dieses Token bekommst du beim Erstellen des Uploads. Den Ablauf findest du unter Uploads.
Idempotenz
curl -X POST https://api.transcript1.com/v1/transcriptions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
-H "Idempotency-Key: 5f1c2b0e-8d4a-4c3e-9b7a-2e6f1d0c9a8b" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'Mit dem Header Idempotency-Key kannst du POST /v1/transcriptions wiederholen, ohne doppelt zu zahlen.
Wähle einen Wert mit 1 bis 255 Zeichen. Er gehört in den Request-Header, nicht in die Query-Parameter.
- Sendest du denselben Key mit denselben Parametern, bekommst du die ursprüngliche Transkription zurück. Es fallen keine weiteren Minuten an.
- Änderst du die Parameter, behältst aber den Key bei, antwortet die API mit
409 idempotency_conflict. - Wiederholst du den Request zum Erstellen eines Chunk-Uploads mit demselben Key, bekommst du ein neues Upload-Token.
Fehler
{
"error": {
"code": "insufficient_minutes",
"message": "Not enough minutes",
"retryable": false,
"action_url": "https://transcript1.com/dashboard/billing",
"minutes_needed": 20,
"minutes_remaining": 3
}
}- Request abgelehnt: Die API hat den Aufruf nicht angenommen.
- Transkription fehlgeschlagen: Die API hat den Job angenommen, konnte ihn aber nicht abschließen. Sein
statussteht auffailed.
Aufbau einer Fehlerantwort
Wenn ein /v1-Request scheitert, enthält die Antwort ein error-Objekt mit code, message und retryable.
- Steuere deine Fehlerbehandlung über
code. - Nutze
messagezur Anzeige. Verlasse dich im Code nicht auf den Wortlaut, denn er kann sich ändern. - Steht
retryableauftrue, kann derselbe Request später funktionieren.
Je nach Fehler stehen neben retryable weitere Felder:
| Feld | Fehlercode | Inhalt |
|---|---|---|
action_url | insufficient_minutes | Link zum Kauf eines Tarifs oder zusätzlicher Minuten. |
minutes_needed, minutes_remaining | insufficient_minutes | Benötigte und verfügbare Minuten. Ist der Bedarf noch unbekannt, steht dort null. |
supported_platforms | unsupported_platform | Liste der unterstützten Plattformen: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"]. |
missing_chunks | upload_incomplete | Indizes der noch fehlenden Chunks. |
max_bytes | media_too_large | Deine maximal erlaubte Dateigröße in Bytes. |
params | invalid_request | Bei ungültigen Body- oder Query-Parametern ein Objekt mit den betroffenen Feldern und dem jeweiligen Problem. |
Fehler beim Request
| HTTP-Status | code | Erneut versuchen? | Ursache |
|---|---|---|---|
| 400, 422 | invalid_request | Nein | Ein Parameter oder Body-Feld fehlt oder ist ungültig. Dazu gehören unzulässige Kombinationen wie diarize mit source: "captions" oder reuse_recent mit webhook_url. Auch ungültiges JSON im Request-Body löst diesen Fehler aus. |
| 422 | invalid_url | Nein | Der Link muss zu YouTube, X, TikTok, Instagram, Facebook, Bluesky oder einer anderen unter Quellen und Einstellungen genannten Plattform führen. Der Fehler tritt auch auf, wenn platform nicht zum Link passt. |
| 422 | unsupported_platform | Nein | Der Wert für platform wird nicht unterstützt. |
| 422 | unsupported_media | Nein | Die Dateiendung des Uploads wird nicht unterstützt. |
| 401 | unauthorized | Nein | Die Zugangsdaten fehlen, sind ungültig, abgelaufen oder widerrufen. Auch ein ungültiges oder abgelaufenes Upload-Token löst diesen Fehler aus. |
| 403 | insufficient_scope | Nein | Dem OAuth-Token fehlt der erforderliche Scope transcriptions:read oder transcriptions:write. |
| 403 | email_not_verified | Nein | Deine E-Mail-Adresse ist noch nicht bestätigt. Bestätige sie, bevor du eine Transkription startest. |
| 402 | insufficient_minutes | Nein | Du hast 0 nutzbare Minuten oder dein Guthaben reicht für die Transkription nicht aus. |
| 402 | no_active_plan | Nein | Du versuchst, ohne aktiven kostenpflichtigen Tarif Minuten aufzuladen. Diesen Code liefert nur der Billing-Endpoint des Dashboards, nicht die Transkriptions-API. |
| 404 | not_found | Nein | Es gibt keine Transkription mit dieser ID oder sie gehört einem anderen Team. |
| 409 | upload_incomplete | Nein | Chunks fehlen noch. Lade sie hoch und schließe den Upload danach erneut ab. |
| 409 | invalid_state | Nein | Der Job wartet nicht auf einen Upload. Du kannst weder weitere Chunks senden noch den Upload abschließen. |
| 409 | idempotency_conflict | Nein | Du hast diesen Idempotency-Key schon mit anderen Parametern verwendet. |
| 413 | media_too_large | Nein | Deine Datei überschreitet das Größenlimit. Details stehen unter Uploads. |
| 413 | invalid_request | Nein | Der JSON-Body ist größer als 1 MB. Bei zu großen Dateien lautet der Code stattdessen media_too_large. |
| 429 | rate_limited | Ja | Du hast das Request-Limit pro Minute überschritten. Warte so viele Sekunden, wie Retry-After angibt. |
| 429 | too_many_active_jobs | Ja | Es laufen zu viele Transkriptionen oder es sind zu viele Uploads offen. Warte die in Retry-After angegebenen Sekunden. |
| 500 | internal_error | Ja | Bei transcript1 ist ein interner Fehler aufgetreten. Versuche es nach einer kurzen Pause erneut. |
| 503 | service_unavailable | Ja | transcript1 ist gerade nicht erreichbar. Versuche es nach einer kurzen Pause erneut. |
Bei Browser-Requests mit Login-Cookie prüft die API zusätzlich die Herkunft. Kommt der Request von einer anderen Website, antwortet sie mit 403 forbidden_origin.
Für API-Aufrufe mit Authorization-Header gilt diese Prüfung nicht.
Bei jeder 429-Antwort gibt der Header Retry-After die Wartezeit in Sekunden an. Die konkreten Limits findest du unter Tarife und Limits.
Wann du einen Request wiederholen solltest
- Wiederhole den Request nur bei
retryablemit dem Werttrue. Das gilt fürrate_limited,too_many_active_jobs,internal_errorundservice_unavailable. - Richte dich nach
Retry-After, falls der Header vorhanden ist. Verlängere andernfalls die Pause zwischen den Versuchen. - Behalte bei einem erneuten
POST /v1/transcriptionsdenselbenIdempotency-Key-Header bei. So zahlst du nicht doppelt, falls der erste Request doch erfolgreich war. - Bei allen anderen Codes musst du zuerst den Request korrigieren.
Fehler während der Transkription
Scheitert die Verarbeitung, steht status auf failed. Im Job findest du dann ein error-Objekt mit code und message.
Das kann passieren, nachdem die API deinen Request bereits beantwortet hat. Prüfe deshalb beim Polling und bei einem transcription.failed-Webhook das Feld error.
error.code | Bedeutung | Erneut versuchen? |
|---|---|---|
media_unavailable | Das Video fehlt oder ist nicht mehr abrufbar. | Nein. Kontrolliere die URL. |
media_private | Das Video ist privat. | Nein. Nimm einen öffentlichen Post oder ein öffentliches bzw. nicht gelistetes YouTube-Video. |
login_required | Die Plattform verlangt eine Anmeldung. | Nein. Dauerhafter Fehler, kein automatischer neuer Versuch. |
no_audio | Das Video enthält keine Tonspur. | Nein. Dauerhafter Fehler, kein automatischer neuer Versuch. |
age_restricted | Für das Video gilt eine Altersbeschränkung. | Nein. Lade stattdessen die Datei hoch. |
live_stream | Der Stream läuft noch oder hat noch nicht begonnen. | Erst nach Ende des Streams. |
no_captions | Du hast source auf captions gesetzt, aber es gibt keine verwendbaren Untertitel. | Wechsle zu source: "auto" oder "stt". |
media_too_long | Die Aufnahme überschreitet das Speech-to-Text-Limit von 5 Stunden. | Nein. Teile die Aufnahme in kürzere Dateien auf. |
unsupported_media | Die Datei lässt sich nicht lesen oder enthält keine Audiospur. | Nein. Lade eine unterstützte Datei hoch. |
no_speech | In der Aufnahme wurde keine gesprochene Sprache erkannt. | Nein. |
unsupported_language | Die Sprache der Aufnahme wird nicht unterstützt. | Nein. Gib für language einen unterstützten Sprachcode oder "auto" an. |
insufficient_minutes | Als die Transkription abgerechnet werden sollte, reichte dein Guthaben nicht mehr. | Nach einer Aufladung. |
fetch_failed | Auch nach mehreren Versuchen konnte transcript1 die Aufnahme nicht abrufen. | Ja, zu einem späteren Zeitpunkt. |
transcription_failed | Speech-to-Text ist auch nach mehreren Versuchen fehlgeschlagen. | Ja, zu einem späteren Zeitpunkt. |
Wenn eine Transkription fehlschlägt oder du sie abbrichst, zahlst du nichts. Bereits abgezogene Minuten bekommst du zurück.
Objekt Transcription
{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "completed",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": {
"title": "Example video",
"author": "Example channel",
"author_url": "https://www.youtube.com/@examplechannel",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"duration_seconds": 212
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": "youtube_captions",
"caption_kind": "auto_generated",
"language": "en",
"billed_minutes": 4,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": "2026-10-13T09:30:04.000Z",
"deleted_at": null,
"transcript": {
"text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
"speakers": [],
"segments": [
{
"start": 0,
"end": 4.2,
"speaker": null,
"language": "en",
"text": "Welcome back to the channel."
},
{
"start": 4.2,
"end": 9.8,
"speaker": null,
"language": "en",
"text": "Today we are looking at how transcripts are made."
}
]
}
}Felder
- idstringPflichtfeld
- objectstringPflichtfeld
Mögliche Werte
transcription - statusstringPflichtfeld
Mögliche Werte
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - sourceobjectPflichtfeld
Felder anzeigenFelder ausblenden
With
url- platformstringPflichtfeld
- urlstringPflichtfeldnull erlaubt
- media_idstringPflichtfeldnull erlaubt
With
filename- platformstringPflichtfeld
Mögliche Werte
upload - filenamestringPflichtfeldnull erlaubt
- sizenumberPflichtfeldnull erlaubt
- mediaobjectPflichtfeldnull erlaubt
Felder anzeigenFelder ausblenden
- titlestringoptionalnull erlaubt
- authorstringoptionalnull erlaubt
- author_urlstringoptionalnull erlaubt
- thumbnail_urlstringoptionalnull erlaubt
- filenamestringoptional
- mime_typestringoptional
- sizenumberoptional
- duration_secondsnumberoptionalnull erlaubt
- optionsobjectPflichtfeld
Felder anzeigenFelder ausblenden
- sourcestringPflichtfeld
Mögliche Werte
autocaptionsstt - languagestringPflichtfeld
- diarizebooleanPflichtfeld
- transcript_sourcestringPflichtfeldnull erlaubt
Mögliche Werte
youtube_captionsx_captionsspeech_to_text - caption_kindstringPflichtfeldnull erlaubt
Mögliche Werte
manualauto_generated - languagestringPflichtfeldnull erlaubt
- billed_minutesintegerPflichtfeldnull erlaubt
- cachedbooleanPflichtfeld
- errorobjectPflichtfeldnull erlaubt
Felder anzeigenFelder ausblenden
- codestringPflichtfeld
- messagestringPflichtfeldnull erlaubt
- created_atstring · date-timePflichtfeldnull erlaubt
- completed_atstring · date-timePflichtfeldnull erlaubt
- deleted_atstring · date-timePflichtfeldnull erlaubt
When the transcription was deleted. Always
nullunless the request includesinclude_deleted=true. - transcriptobjectoptional
Included in
GETresponses for completed transcriptions.Felder anzeigenFelder ausblenden
- textstringPflichtfeld
- speakersarray of stringsPflichtfeld
- segmentsarray of objectsPflichtfeld
Felder anzeigenFelder ausblenden
- startnumberPflichtfeld
- endnumberPflichtfeld
- speakerstringPflichtfeldnull erlaubt
- languagestringPflichtfeldnull erlaubt
- textstringPflichtfeld
- wordsarray of objectsoptionalnull erlaubt
Felder anzeigenFelder ausblenden
- startnumberPflichtfeld
- endnumberPflichtfeld
- textstringPflichtfeld
- confidencenumberoptionalnull erlaubt
- speakerstringPflichtfeldnull erlaubt
Transcriptions
Create, read, list and delete transcriptions of video links and uploaded files.
Create a transcription
POST /v1/transcriptionsTranscribes an uploaded file or a YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat link. Social posts must be public; only the first video is used.
Twitch accepts clips and completed VODs; Pinterest accepts native video pins; LinkedIn accepts public video posts; Snapchat accepts Spotlight. New platforms require public recorded native media without supplied account cookies; no live streams or external embeds.
platform is optional and detected from the link. Captions are YouTube/X-only. All sources use the same per-minute price.
Returns 200 for a final result, 201 when awaiting an upload and 202 while processing. Pass wait to wait up to 60 seconds for a final status.
Pass reuse_recent=true with a platform URL to reuse a transcription of the same video and options from the last 24 hours. The response includes reused: true, with no additional charge. Short links may not be matched. An Idempotency-Key replay takes precedence.
curl -X POST https://api.transcript1.com/v1/transcriptions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "queued",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": null,
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": null,
"caption_kind": null,
"language": null,
"billed_minutes": null,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": null,
"deleted_at": null,
"reused": false
}Query
- waitintegeroptionalnull erlaubtStandard: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- reuse_recentstringoptionalStandard: false
For platform URLs only. Cannot be combined with
webhook_url. Returns the team's most recent transcription of the same video with the samesource,languageanddiarize. It must have been created in the last 24 hours and must not befailedorcanceled. Reuses that transcription without creating or charging for a new one.Mögliche Werte
truefalse
Headers
- Idempotency-Keystringoptional
Returns the original transcription when you repeat a request with the same key and parameters.
Body
With url
- platformstringoptional
Optional; detected from the link. If supplied, must match the link.
Mögliche Werte
youtubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat - urlstringPflichtfeld
YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat video link. Social posts must be public; only the first video is used. New platforms support public recorded native video without supplied account cookies, not live streams or external embeds.
- sourcestringoptional
"auto" uses YouTube or X captions when available, otherwise speech-to-text. "captions" is YouTube/X-only. "stt" always uses speech-to-text.
Mögliche Werte
autocaptionsstt - languagestringoptional
- diarizebooleanoptional
- webhook_urlstring · urioptional
With upload
- uploadobjectPflichtfeld
Felder anzeigenFelder ausblenden
- filenamestringPflichtfeld
- sizeintegerPflichtfeld
- mime_typestringoptional
- sourcestringoptional
Mögliche Werte
autostt - languagestringoptional
- diarizebooleanoptional
- webhook_urlstring · urioptional
multipart/form-data
- filestring · binaryPflichtfeld
- sourcestringoptional
Mögliche Werte
autostt - languagestringoptional
- diarizebooleanoptional
- webhook_urlstring · urioptional
Erfolgreiche Antwort
200 OK— The transcription reached a final status201 Created— The transcription is awaiting a chunked upload.uploaddescribes where to send each chunk.202 Accepted— The transcription is processing
Weitere Statuscodes
400 Bad Request— The JSON request body is malformed401 Unauthorized— Missing or invalid credential402 Payment Required— Not enough minutes. Applies to every request body format.403 Forbidden— Insufficient scope, unverified email, cross-site request or a single-member team plan409 Conflict—Idempotency-Keywas reused with different parameters.413 Content Too Large— The upload or the JSON request body is too large422 Unprocessable Content— Invalid request, URL, platform or file type429 Too Many Requests— Too many requests or too many active transcriptions. Wait for the number of seconds inRetry-After.
Get a transcription
GET /v1/transcriptions/{id}Returns the transcription object. Completed transcriptions include the transcript. Use format to render it as text, SRT or WebVTT.
If you request a rendered format before completion, the response is JSON. It returns 202 while processing or 200 if the transcription failed or was canceled.
With include_deleted=true, a deleted transcription is returned as a record without a transcript. For deleted records, wait is ignored and rendered formats return 404.
curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "completed",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": {
"title": "Example video",
"author": "Example channel",
"author_url": "https://www.youtube.com/@examplechannel",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"duration_seconds": 212
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": "youtube_captions",
"caption_kind": "auto_generated",
"language": "en",
"billed_minutes": 4,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": "2026-10-13T09:30:04.000Z",
"deleted_at": null,
"transcript": {
"text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
"speakers": [],
"segments": [
{
"start": 0,
"end": 4.2,
"speaker": null,
"language": "en",
"text": "Welcome back to the channel."
},
{
"start": 4.2,
"end": 9.8,
"speaker": null,
"language": "en",
"text": "Today we are looking at how transcripts are made."
}
]
}
}Path
- idstringPflichtfeld
Query
- waitintegeroptionalnull erlaubtStandard: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- formatstringoptionalStandard: json
Mögliche Werte
jsontextsrtvtt - includestringoptional
Mögliche Werte
words - timestampsstringoptionalStandard: false
Mögliche Werte
truefalse - include_deletedstringoptionalStandard: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Mögliche Werte
truefalse
Erfolgreiche Antwort
200 OK— The transcription, or the rendered transcript202 Accepted— A rendered format was requested while the transcription is still processing
Weitere Statuscodes
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— Transcription not found422 Unprocessable Content— Invalid query parameters429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
List transcriptions
GET /v1/transcriptionsLists your team's transcriptions, newest first, without transcripts. Pass next_cursor as cursor to get the next page.
Deleted transcriptions are hidden unless you pass include_deleted=true.
curl https://api.transcript1.com/v1/transcriptions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"object": "list",
"data": [
{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "completed",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": {
"title": "Example video",
"author": "Example channel",
"author_url": "https://www.youtube.com/@examplechannel",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"duration_seconds": 212
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": "youtube_captions",
"caption_kind": "auto_generated",
"language": "en",
"billed_minutes": 4,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": "2026-10-13T09:30:04.000Z",
"deleted_at": null
}
],
"next_cursor": "eyJjcmVhdGVkQXQiOiIyMDI2LTEwLTEzVDA5OjMwOjAwLjAwMFoiLCJpZCI6IjViMGU2YzFjLTBkNmYtNGMxZi05YjVlLThmM2Y0ZDBhN2ExMSJ9"
}Query
- limitintegeroptionalStandard: 20
- cursorstringoptional
- statusstringoptional
Mögliche Werte
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - platformstringoptional
- include_deletedstringoptionalStandard: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Mögliche Werte
truefalse
Erfolgreiche Antwort
200 OK— A page of transcriptions
Weitere Statuscodes
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan422 Unprocessable Content— Invalid query parameters or cursor429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Cancel or delete a transcription
DELETE /v1/transcriptions/{id}Cancels an active transcription or one awaiting an upload. Any charged minutes are refunded.
Deletes a completed, failed or canceled transcription together with its stored transcript.
curl -X DELETE https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"Antwort ohne Body
Path
- idstringPflichtfeld
Erfolgreiche Antwort
204 No Content— Canceled or deleted
Weitere Statuscodes
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope, cross-site request or a single-member team plan404 Not Found— Transcription not found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Uploads
Send a large file in chunks. Start with Create a transcription using an upload body. Send each chunk, then complete the upload.
Upload one chunk
PUT /v1/transcriptions/{id}/upload/chunks/{index}Sends one chunk of a chunked upload as application/octet-stream.
Authenticate with the X-Upload-Token from the create response. This endpoint does not use a bearer credential.
The body must match the planned chunk size exactly. Sending a chunk again replaces it.
curl -X PUT https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/chunks/0 \
-H "X-Upload-Token: t1u_Zk3vYb8Nw1ZrT5xLpD0aHcE7sUj9GfMiO2yXtVnR4Bw" \
-H "Content-Type: application/octet-stream" \
--data-binary @chunk-0.bin{
"object": "upload_chunk",
"index": 0,
"size": 838860
}Path
- idstringPflichtfeld
- indexintegeroptionalnull erlaubt
Zero-based chunk number from the upload plan
Headers
- X-Upload-TokenstringPflichtfeld
Body
- (Raw Body)string · binaryPflichtfeld
Erfolgreiche Antwort
200 OK— The chunk was stored
Weitere Statuscodes
401 Unauthorized— Missing, invalid or expired upload token404 Not Found— Transcription not found409 Conflict— The transcription is no longer awaiting an upload422 Unprocessable Content— Index outside the chunk plan or wrong body size
Get upload progress
GET /v1/transcriptions/{id}/uploadLists the received chunks of a chunked upload so an interrupted upload can resume.
curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"object": "upload",
"status": "awaiting_upload",
"total_bytes": 48210334,
"uploaded_bytes": 4194300,
"received_chunks": [
0,
1,
2,
3,
4
],
"chunks": [
{
"index": 0,
"offset": 0,
"size": 838860
},
{
"index": 1,
"offset": 838860,
"size": 838860
},
{
"index": 2,
"offset": 1677720,
"size": 838860
},
{
"index": 3,
"offset": 2516580,
"size": 838860
},
{
"index": 4,
"offset": 3355440,
"size": 838860
},
{
"index": 5,
"offset": 4194300,
"size": 8388608
},
{
"index": 6,
"offset": 12582908,
"size": 8388608
},
{
"index": 7,
"offset": 20971516,
"size": 8388608
},
{
"index": 8,
"offset": 29360124,
"size": 8388608
},
{
"index": 9,
"offset": 37748732,
"size": 8388608
},
{
"index": 10,
"offset": 46137340,
"size": 2072994
}
],
"expires_at": "2026-10-13T10:30:00.000Z"
}Path
- idstringPflichtfeld
Erfolgreiche Antwort
200 OK— Upload progress
Weitere Statuscodes
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— Transcription or upload not found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Complete a chunked upload
POST /v1/transcriptions/{id}/upload/completeChecks that every planned chunk arrived with the planned size. Then assembles the file and queues the transcription.
curl -X POST https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/complete \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "queued",
"source": {
"platform": "upload",
"filename": "interview.m4a",
"size": 48210334
},
"media": {
"filename": "interview.m4a",
"mime_type": "audio/mp4",
"size": 48210334
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": null,
"caption_kind": null,
"language": null,
"billed_minutes": null,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": null,
"deleted_at": null
}Path
- idstringPflichtfeld
Erfolgreiche Antwort
202 Accepted— The transcription is queued
Weitere Statuscodes
401 Unauthorized— Missing or invalid credential402 Payment Required— Not enough minutes to queue the transcription. The upload stays awaiting completion.403 Forbidden— Insufficient scope, cross-site request or a single-member team plan404 Not Found— Transcription or upload not found409 Conflict—upload_incompletemeans chunks are missing.missing_chunkslists them.invalid_statemeans the transcription is not awaiting an upload.429 Too Many Requests— Too many requests or too many active transcriptions. Wait for the number of seconds inRetry-After.
Account
Your plan, remaining minutes and rate limits.
Get your account
GET /v1/accountReturns the authenticated user, current plan and remaining transcription minutes.
curl https://api.transcript1.com/v1/account \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"object": "account",
"user": {
"id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
"email": "[email protected]"
},
"plan": {
"id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
"name": "Monthly"
},
"minutes": {
"remaining": 1180,
"cycle": 980,
"topup": 200,
"topup_usable": true,
"resets_at": "2026-11-13T09:30:00.000Z"
},
"renews_at": "2026-11-13T09:30:00.000Z",
"limits": {
"requests_per_minute": 200,
"max_active_jobs": 10
}
}Erfolgreiche Antwort
200 OK— The authenticated user's account
Weitere Statuscodes
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— No account found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Objekt Account
{
"object": "account",
"user": {
"id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
"email": "[email protected]"
},
"plan": {
"id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
"name": "Monthly"
},
"minutes": {
"remaining": 1180,
"cycle": 980,
"topup": 200,
"topup_usable": true,
"resets_at": "2026-11-13T09:30:00.000Z"
},
"renews_at": "2026-11-13T09:30:00.000Z",
"limits": {
"requests_per_minute": 200,
"max_active_jobs": 10
}
}Felder
- objectstringPflichtfeld
Mögliche Werte
account - userobjectPflichtfeld
Felder anzeigenFelder ausblenden
- idstringPflichtfeld
- emailstringPflichtfeld
- planobjectPflichtfeldnull erlaubt
Felder anzeigenFelder ausblenden
- idstringPflichtfeld
- namestringPflichtfeld
- minutesobjectPflichtfeld
Felder anzeigenFelder ausblenden
- remainingnumberPflichtfeld
- cyclenumberPflichtfeld
- topupnumberPflichtfeld
- topup_usablebooleanPflichtfeld
- resets_atstring · date-timePflichtfeldnull erlaubt
- renews_atstring · date-timePflichtfeldnull erlaubt
- limitsobjectPflichtfeld
Felder anzeigenFelder ausblenden
- requests_per_minutenumberPflichtfeld
- max_active_jobsnumberPflichtfeld
Webhooks
import { Buffer } from 'node:buffer';
import crypto from 'node:crypto';
// header: X-Transcript1-Signature, rawBody: the request body exactly as received,
// secret: your full signing secret including the whsec_ prefix.
export const verifySignature = (secret, header, rawBody, nowSeconds = Math.floor(Date.now() / 1000)) => {
const fields = Object.fromEntries(header.split(',').map(part => part.trim().split('=')));
const t = Number(fields.t);
if (!Number.isInteger(t) || !fields.v1 || Math.abs(nowSeconds - t) > 300) {
return false;
}
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return fields.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(fields.v1), Buffer.from(expected));
};{
"type": "transcription.completed",
"created_at": "2026-10-13T09:30:04.000Z",
"data": {
"id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
"object": "transcription",
"status": "completed",
"transcript_source": "youtube_captions",
"billed_minutes": 4,
"error": null
}
}Gib beim Erstellen eines Jobs webhook_url an. Sobald die Transkription fertig ist oder fehlschlägt, schickt transcript1 einen signierten POST-Request an diese URL.
Webhooks kannst du nur über die REST-API einrichten. Die Tools des MCP-Servers nehmen keinen Webhook-Parameter an.
Die Webhook-URL übergeben
Übergib webhook_url beim Aufruf von POST /v1/transcriptions:
- Für einen unterstützten Video-Link oder zum Vorbereiten eines Chunk-Uploads gehört die URL in den JSON-Body.
- Bei einem Upload in einem einzigen Request übergibst du sie als Formularfeld.
Für die URL gelten diese Voraussetzungen:
- Sie verwendet HTTPS.
- Sie enthält weder Benutzernamen noch Passwort.
- Die DNS-Auflösung liefert eine öffentliche Adresse.
Private Adressen, Loopback, Link-Local und andere nicht öffentliche Adressen lehnt die API mit 422 invalid_request ab. transcript1 prüft die Adresse beim Erstellen des Jobs und vor jeder Zustellung.
Du kannst webhook_url nicht zusammen mit reuse_recent=true verwenden.
Events
type | Auslöser |
|---|---|
transcription.completed | Der Job hat den Status completed. |
transcription.failed | Der Job hat den Status failed. In data.error stehen code und message. |
Wenn du eine Transkription abbrichst, verschickt transcript1 keinen Webhook.
Inhalt des Webhooks
Der Webhook enthält einen JSON-Body:
created_atgibt an, wann das Event entstanden ist.- In
datasteht das Transkriptionsobjekt, allerdings ohnetranscript.
Den Text holst du mit GET /v1/transcriptions/:id. Nutze dafür die id aus data.
transcript1 sendet diese Header mit:
Content-Type: application/jsonUser-Agent: transcript1-webhooks/1.0X-Transcript1-Signaturezur Signaturprüfung, siehe den nächsten Abschnitt.
Signatur prüfen
Prüfe bei jedem Webhook den Header X-Transcript1-Signature. Sein Format lautet t=<unix seconds>,v1=<hex digest>.
tenthält den Zeitpunkt der Signatur als Unix-Zeit in Sekunden.v1ist der HMAC-SHA256 von"<t>.<raw body>", hexadezimal kodiert. Setze dafür den Wert vont, einen Punkt und den unveränderten Request-Body zusammen. Als HMAC-Schlüssel nimmst du dein vollständiges Signing Secret, inklusivewhsec_.
Berechne die Signatur aus den unveränderten Body-Bytes, bevor du das JSON parst. Wenn du den Body erst parst und wieder serialisierst, können sich Leerzeichen oder die Reihenfolge der Schlüssel ändern. Die Signatur stimmt dann nicht mehr.
- Verwende für den Signaturvergleich eine Funktion mit konstanter Laufzeit.
- Lehne Requests ab, wenn
tzu weit von der aktuellen Uhrzeit abweicht. In den Beispielen sind 5 Minuten erlaubt. - Lies den Body in Express als unverarbeiteten Text ein, ohne ihn vorher zu parsen.
- In Flask übergibst du
request.get_data(as_text=True)alsraw_body.
Signing Secret verwalten
Dein Team hat ein gemeinsames Signing Secret mit dem Präfix whsec_. Damit signiert transcript1 die Webhooks für alle Transkriptionen des Teams.
Unter „Webhooks“ im Dashboard kannst du es anzeigen oder durch ein neues ersetzen.
Der Wechsel gilt sofort. Alle danach gesendeten Webhooks verwenden das neue Secret.
Zustellung und erneute Versuche
- Antwortet dein Server mit
2xx, gilt der Webhook als zugestellt. - Bei einem anderen Statuscode, einem Verbindungsfehler oder ohne Antwort innerhalb von 10 Sekunden gilt der Versuch als fehlgeschlagen.
- Nach einem fehlgeschlagenen Versuch wartet transcript1 mit exponentiellem Backoff und versucht es erneut. Insgesamt gibt es höchstens 6 Versuche. Die Pausen betragen 2, 4, 8, 16 und 32 Minuten. Der letzte Versuch liegt etwa eine Stunde nach dem ersten.
- Verwende die endgültige Ziel-URL. transcript1 folgt keinen Weiterleitungen. Auch
3xxzählt als fehlgeschlagener Versuch. - Dein Server kann denselben Webhook mehrfach erhalten, etwa wenn er erst nach dem Timeout antwortet. Erkenne Duplikate anhand von
data.idundtypeund verarbeite sie nicht erneut.