DocumentatieREST API-referentie
REST API-referentie
Zoek parameters en voorbeelden voor elk /v1-endpoint op. De OpenAPI-specificatie is beschikbaar als JSON en YAML.
Inleiding
https://api.transcript1.com/v1Gebruik https://api.transcript1.com/v1 als basis-URL voor je requests.
- Je verstuurt en ontvangt JSON, met veldnamen in
snake_case. - Datums en tijdstempels gebruiken ISO 8601 in UTC, zoals
2026-10-13T09:30:00.000Z. - De duur van een opname en de tijden in een transcript zijn in seconden.
- Foutresponses hebben een vaste opbouw. Die vind je onder Fouten.
- Elke response op een geauthenticeerd request bevat
X-RateLimit-LimitenX-RateLimit-Remaining. Lees meer over minuten, abonnementen en limieten. - Met
source=autoproberen we op YouTube, X, TikTok, Bluesky, Dailymotion en LinkedIn eerst bruikbare ondertitels in de oorspronkelijke taal. Zijn die er niet, dan gebruiken we speech-to-text. Niet elke video heeft ondertitels. Metcaptions(alleen ondertitels) halen we nooit audio op en schakelen we niet over op STT. Zonder bruikbaar spoor krijg jeno_captions. Metsource=sttof sprekerherkenning kies je meteen voor audio. In Bronnen en instellingen lees je welke beperkingen gelden en wanneer je tijdstempels per segment of per woord krijgt.
Wil je de API-specificatie in je eigen tooling gebruiken? Download OpenAPI als:
Authenticatie
curl https://api.transcript1.com/v1/account \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"Geef je API-sleutel of token mee in de Authorization-header, als Authorization: Bearer <token>.
- API-sleutel: begint met
t1_en geeft toegang tot alle endpoints. Maak een sleutel aan op “API keys”. - OAuth-toegangstoken: begint met
t1o_. Clients zoals Claude en ChatGPT krijgen dit wanneer je ze via MCP koppelt. Het token gebruikt de scopestranscriptions:readentranscriptions:write.
| Scope | Toegang tot |
|---|---|
transcriptions:read | GET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload en GET /v1/account |
transcriptions:write | POST /v1/transcriptions, DELETE /v1/transcriptions/:id en POST /v1/transcriptions/:id/upload/complete |
- Mist het token de scope die je nodig hebt, dan krijg je
403 insufficient_scope. - Ontbreekt de sleutel of het token, of is die ongeldig, dan krijg je
401 unauthorized.
Voor het versturen van chunks gebruik je een ander token. Geef bij PUT /v1/transcriptions/:id/upload/chunks/:index de header X-Upload-Token mee. Je krijgt dat token bij het aanmaken van de upload. De stappen staan bij Bestanden uploaden.
Idempotentie
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"}'Geef bij POST /v1/transcriptions een Idempotency-Key-header mee. Dan kun je het request veilig herhalen.
Kies een waarde van 1 tot 255 tekens en stuur die als header, niet als queryparameter.
- Herhaal je het request met dezelfde sleutel en parameters, dan krijg je de oorspronkelijke transcriptie terug en betaal je niet opnieuw.
- Gebruik je dezelfde sleutel met andere parameters, dan krijg je
409 idempotency_conflict. - Maak je een upload in chunks aan, dan levert hetzelfde request met dezelfde sleutel een nieuw uploadtoken op.
Foutmeldingen
{
"error": {
"code": "insufficient_minutes",
"message": "Not enough minutes",
"retryable": false,
"action_url": "https://transcript1.com/dashboard/billing",
"minutes_needed": 20,
"minutes_remaining": 3
}
}- Een fout bij een request betekent dat de API je aanroep meteen weigert.
- Een fout tijdens de transcriptie ontstaat pas nadat de API die heeft geaccepteerd. Dan staat
statusopfailed.
Zo ziet een foutresponse eruit
Bij een geweigerd /v1-request krijg je een error-object met code, message en retryable.
- Laat je code reageren op
code. - Gebruik
messagevoor de uitleg aan de gebruiker. De tekst kan veranderen. - Staat
retryableoptrue, dan kan hetzelfde request later wel slagen.
Afhankelijk van de fout krijg je naast retryable extra velden:
| Veld | Foutcode | Wat staat erin? |
|---|---|---|
action_url | insufficient_minutes | De link om een abonnement te nemen of minuten bij te kopen. |
minutes_needed, minutes_remaining | insufficient_minutes | Het aantal benodigde minuten en je huidige saldo. Het benodigde aantal is null zolang de duur nog onbekend is. |
supported_platforms | unsupported_platform | De toegestane platforms: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"]. |
missing_chunks | upload_incomplete | De indexen van de chunks die de server nog mist. |
max_bytes | media_too_large | Jouw maximale bestandsgrootte in bytes. |
params | invalid_request | Bij ongeldige body- of queryvelden een object dat per veld uitlegt wat je moet aanpassen. |
Wanneer de API je request weigert
| HTTP-status | code | Opnieuw proberen? | Oorzaak |
|---|---|---|---|
| 400, 422 | invalid_request | Nee | Een parameter of bodyveld ontbreekt of is ongeldig. Dit geldt ook voor ongeldige JSON en combinaties zoals diarize met source: "captions" of reuse_recent met webhook_url. |
| 422 | invalid_url | Nee | De link is niet geldig of komt niet overeen met platform. Gebruik YouTube, X, TikTok, Instagram, Facebook, Bluesky of een ander platform uit Bronnen en instellingen. |
| 422 | unsupported_platform | Nee | De opgegeven waarde voor platform is niet toegestaan. |
| 422 | unsupported_media | Nee | De bestandsextensie wordt niet ondersteund. |
| 401 | unauthorized | Nee | Je API-sleutel of token ontbreekt, is ongeldig, verlopen of ingetrokken. Ook een ongeldig of verlopen uploadtoken geeft deze fout. |
| 403 | insufficient_scope | Nee | Het OAuth-token heeft geen transcriptions:read of transcriptions:write waar die scope nodig is. |
| 403 | email_not_verified | Nee | Je moet eerst je e-mailadres bevestigen om transcripties te kunnen starten. |
| 402 | insufficient_minutes | Nee | Je beschikbare saldo is 0 of je hebt te weinig minuten voor deze transcriptie. |
| 402 | no_active_plan | Nee | Je wilt minuten bijkopen, maar hebt geen actief betaald abonnement. Alleen het billing-endpoint van het dashboard geeft deze fout, niet de transcriptie-endpoints. |
| 404 | not_found | Nee | De transcriptie bestaat niet of is van een ander team. |
| 409 | upload_incomplete | Nee | De server mist nog chunks. Verstuur die en rond de upload daarna opnieuw af. |
| 409 | invalid_state | Nee | De transcriptie wacht niet meer op een upload. Je kunt geen chunks meer sturen en de upload niet meer afronden. |
| 409 | idempotency_conflict | Nee | Je hebt deze Idempotency-Key eerder met andere parameters gebruikt. |
| 413 | media_too_large | Nee | Het bestand overschrijdt je uploadlimiet. Bekijk Bestanden uploaden. |
| 413 | invalid_request | Nee | De JSON-body is groter dan 1 MB. Bij te grote bestanden krijg je media_too_large. |
| 429 | rate_limited | Ja | Je stuurt te veel requests per minuut. Wacht het aantal seconden uit Retry-After. |
| 429 | too_many_active_jobs | Ja | Er lopen te veel transcripties of er staan te veel uploads open. Wacht het aantal seconden uit Retry-After. |
| 500 | internal_error | Ja | Er gaat iets mis bij transcript1. Probeer het na een korte pauze opnieuw. |
| 503 | service_unavailable | Ja | transcript1 is even niet beschikbaar. Probeer het na een korte pauze opnieuw. |
Bij browserrequests met een inlogcookie controleren we ook vanaf welke site het request komt. Een andere site krijgt 403 forbidden_origin.
Voor API-requests met een Authorization-header geldt die controle niet.
In elke 429-response staat Retry-After: het aantal seconden dat je moet wachten. De limieten zelf vind je bij Minuten, abonnementen en limieten.
Wanneer stuur je een request opnieuw?
- Herhaal alleen requests met
retryableoptrue:rate_limited,too_many_active_jobs,internal_errorenservice_unavailable. - Houd de wachttijd uit
Retry-Afteraan. Ontbreekt die header, wacht dan bij elke poging langer. - Gebruik bij het herhalen van
POST /v1/transcriptionsdezelfdeIdempotency-Key-header. Zo betaal je niet dubbel als het eerste request toch is gelukt. - Pas bij alle andere codes eerst je request aan voordat je het nog eens verstuurt.
Als een transcriptie later mislukt
Loopt een transcriptie vast, dan staat status op failed. In het error-object staan code en message.
Ook na een geslaagd request kan de transcriptie nog mislukken. Controleer error dus tijdens het pollen of bij een webhook van het type transcription.failed.
error.code | Wat is er mis? | Wat kun je doen? |
|---|---|---|
media_unavailable | De video bestaat niet of is niet meer beschikbaar. | Niet opnieuw proberen. Controleer de URL. |
media_private | De video is afgeschermd. | Gebruik een openbare post of een openbare of verborgen YouTube-video. Hetzelfde request herhalen helpt niet. |
login_required | Je moet op het platform ingelogd zijn om de video te bekijken. | Niet opnieuw proberen. Deze fout is definitief; we proberen het niet automatisch opnieuw. |
no_audio | Er zit geen audiospoor in de video. | Niet opnieuw proberen. Deze fout is definitief; we proberen het niet automatisch opnieuw. |
age_restricted | De video heeft een leeftijdsbeperking. | Hetzelfde request herhalen helpt niet. Upload het bestand. |
live_stream | De uitzending loopt nog of is nog niet begonnen. | Probeer het na afloop van de uitzending. |
no_captions | Je zette source op captions, maar er is geen bruikbaar ondertitelspoor. | Kies source: "auto" of "stt". |
media_too_long | De opname duurt langer dan de 5 uur die speech-to-text aankan. | Splits het bestand. Hetzelfde request herhalen helpt niet. |
unsupported_media | Het bestand is niet leesbaar of heeft geen audiospoor. | Stuur een ondersteund bestand; herhaal niet hetzelfde request. |
no_speech | Geen spraak gedetecteerd. | Niet opnieuw proberen. |
unsupported_language | We ondersteunen de taal van deze opname niet. | Kies voor language een ondersteunde code of "auto"; probeer niet opnieuw met dezelfde instelling. |
insufficient_minutes | Je saldo was op voordat we de minuten konden afschrijven. | Koop eerst minuten bij en probeer het dan opnieuw. |
fetch_failed | Ook na meerdere pogingen konden we de opname niet ophalen. | Probeer het later opnieuw. |
transcription_failed | Ook na meerdere pogingen lukte speech-to-text niet. | Probeer het later opnieuw. |
Mislukt een transcriptie of annuleer je die, dan betaal je niets. Hadden we al minuten afgeschreven, dan krijg je die terug.
Object 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."
}
]
}
}Velden
- idstringverplicht
- objectstringverplicht
Toegestane waarden
transcription - statusstringverplicht
Toegestane waarden
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - sourceobjectverplicht
Geneste velden tonenGeneste velden verbergen
With
url- platformstringverplicht
- urlstringverplichtnullable
- media_idstringverplichtnullable
With
filename- platformstringverplicht
Toegestane waarden
upload - filenamestringverplichtnullable
- sizenumberverplichtnullable
- mediaobjectverplichtnullable
Geneste velden tonenGeneste velden verbergen
- titlestringoptioneelnullable
- authorstringoptioneelnullable
- author_urlstringoptioneelnullable
- thumbnail_urlstringoptioneelnullable
- filenamestringoptioneel
- mime_typestringoptioneel
- sizenumberoptioneel
- duration_secondsnumberoptioneelnullable
- optionsobjectverplicht
Geneste velden tonenGeneste velden verbergen
- sourcestringverplicht
Toegestane waarden
autocaptionsstt - languagestringverplicht
- diarizebooleanverplicht
- transcript_sourcestringverplichtnullable
Toegestane waarden
youtube_captionsx_captionsspeech_to_text - caption_kindstringverplichtnullable
Toegestane waarden
manualauto_generated - languagestringverplichtnullable
- billed_minutesintegerverplichtnullable
- cachedbooleanverplicht
- errorobjectverplichtnullable
Geneste velden tonenGeneste velden verbergen
- codestringverplicht
- messagestringverplichtnullable
- created_atstring · date-timeverplichtnullable
- completed_atstring · date-timeverplichtnullable
- deleted_atstring · date-timeverplichtnullable
When the transcription was deleted. Always
nullunless the request includesinclude_deleted=true. - transcriptobjectoptioneel
Included in
GETresponses for completed transcriptions.Geneste velden tonenGeneste velden verbergen
- textstringverplicht
- speakersarray of stringsverplicht
- segmentsarray of objectsverplicht
Geneste velden tonenGeneste velden verbergen
- startnumberverplicht
- endnumberverplicht
- speakerstringverplichtnullable
- languagestringverplichtnullable
- textstringverplicht
- wordsarray of objectsoptioneelnullable
Geneste velden tonenGeneste velden verbergen
- startnumberverplicht
- endnumberverplicht
- textstringverplicht
- confidencenumberoptioneelnullable
- speakerstringverplichtnullable
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
- waitintegeroptioneelnullableStandaard: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- reuse_recentstringoptioneelStandaard: 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.Toegestane waarden
truefalse
Headers
- Idempotency-Keystringoptioneel
Returns the original transcription when you repeat a request with the same key and parameters.
Body
With url
- platformstringoptioneel
Optional; detected from the link. If supplied, must match the link.
Toegestane waarden
youtubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat - urlstringverplicht
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.
- sourcestringoptioneel
"auto" uses YouTube or X captions when available, otherwise speech-to-text. "captions" is YouTube/X-only. "stt" always uses speech-to-text.
Toegestane waarden
autocaptionsstt - languagestringoptioneel
- diarizebooleanoptioneel
- webhook_urlstring · urioptioneel
With upload
- uploadobjectverplicht
Geneste velden tonenGeneste velden verbergen
- filenamestringverplicht
- sizeintegerverplicht
- mime_typestringoptioneel
- sourcestringoptioneel
Toegestane waarden
autostt - languagestringoptioneel
- diarizebooleanoptioneel
- webhook_urlstring · urioptioneel
multipart/form-data
- filestring · binaryverplicht
- sourcestringoptioneel
Toegestane waarden
autostt - languagestringoptioneel
- diarizebooleanoptioneel
- webhook_urlstring · urioptioneel
Statuscodes
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
Overige 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
- idstringverplicht
Query
- waitintegeroptioneelnullableStandaard: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- formatstringoptioneelStandaard: json
Toegestane waarden
jsontextsrtvtt - includestringoptioneel
Toegestane waarden
words - timestampsstringoptioneelStandaard: false
Toegestane waarden
truefalse - include_deletedstringoptioneelStandaard: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Toegestane waarden
truefalse
Statuscodes
200 OK— The transcription, or the rendered transcript202 Accepted— A rendered format was requested while the transcription is still processing
Overige 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
- limitintegeroptioneelStandaard: 20
- cursorstringoptioneel
- statusstringoptioneel
Toegestane waarden
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - platformstringoptioneel
- include_deletedstringoptioneelStandaard: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Toegestane waarden
truefalse
Statuscodes
200 OK— A page of transcriptions
Overige 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"Geen body in de response
Path
- idstringverplicht
Statuscodes
204 No Content— Canceled or deleted
Overige 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
- idstringverplicht
- indexintegeroptioneelnullable
Zero-based chunk number from the upload plan
Headers
- X-Upload-Tokenstringverplicht
Body
- (raw body)string · binaryverplicht
Statuscodes
200 OK— The chunk was stored
Overige 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
- idstringverplicht
Statuscodes
200 OK— Upload progress
Overige 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
- idstringverplicht
Statuscodes
202 Accepted— The transcription is queued
Overige 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
}
}Statuscodes
200 OK— The authenticated user's account
Overige 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.
Object 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
}
}Velden
- objectstringverplicht
Toegestane waarden
account - userobjectverplicht
Geneste velden tonenGeneste velden verbergen
- idstringverplicht
- emailstringverplicht
- planobjectverplichtnullable
Geneste velden tonenGeneste velden verbergen
- idstringverplicht
- namestringverplicht
- minutesobjectverplicht
Geneste velden tonenGeneste velden verbergen
- remainingnumberverplicht
- cyclenumberverplicht
- topupnumberverplicht
- topup_usablebooleanverplicht
- resets_atstring · date-timeverplichtnullable
- renews_atstring · date-timeverplichtnullable
- limitsobjectverplicht
Geneste velden tonenGeneste velden verbergen
- requests_per_minutenumberverplicht
- max_active_jobsnumberverplicht
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
}
}Met webhook_url laat je je server weten wanneer de transcriptie klaar is. Geef die URL mee bij het aanmaken. Bij een geslaagde of mislukte transcriptie stuurt transcript1 daar een ondertekend POST-request naartoe.
Je kunt webhooks alleen via de REST API instellen. De MCP-server heeft hiervoor geen parameter.
Geef je webhook-URL mee
Bij POST /v1/transcriptions geef je webhook_url op deze manier mee:
- Bij een ondersteunde videolink of het aanmaken van een upload in chunks: in de JSON-body.
- Bij een bestand dat je in één request uploadt: als formulierveld.
Gebruik een URL die:
- HTTPS gebruikt.
- Geen gebruikersnaam of wachtwoord bevat.
- Uitkomt op een openbaar adres.
Verwijst de URL naar een privéadres, loopback-adres, link-local-adres of een ander niet-openbaar adres, dan krijg je 422 invalid_request. We controleren het adres bij het aanmaken en opnieuw bij elke poging om de webhook te versturen.
Je kunt webhook_url niet samen met reuse_recent=true gebruiken.
Welke events ontvang je?
type | Wanneer ontvang je dit? |
|---|---|
transcription.completed | Zodra de status completed is. |
transcription.failed | Zodra de status failed is. In data.error staan de code en message. |
Annuleer je een transcriptie, dan sturen we geen webhook.
Wat staat er in het request?
De webhook heeft een JSON-body met deze velden:
- In
created_atstaat wanneer het event is aangemaakt. - In
datastaat het transcriptieobject, zonder het veldtranscript.
Haal de tekst op met GET /v1/transcriptions/:id. Gebruik daarvoor de id uit data.
Deze headers komen mee:
Content-Type: application/jsonUser-Agent: transcript1-webhooks/1.0X-Transcript1-Signature; hieronder lees je hoe je die controleert.
Controleer de handtekening
In elk webhookrequest staat X-Transcript1-Signature, met de waarde t=<unix seconds>,v1=<hex digest>.
tgeeft in Unix-seconden aan wanneer we het request hebben ondertekend.v1bevat de HMAC-SHA256 als hexwaarde. Bereken die over"<t>.<raw body>":t, een punt en de body zoals je die hebt ontvangen. Gebruik je volledige geheime sleutel, dus inclusiefwhsec_.
Controleer de handtekening op de oorspronkelijke bytes, vóór je de JSON parset. Zet je geparste JSON weer om naar tekst, dan kunnen spaties of de veldvolgorde anders zijn. De handtekening klopt dan niet meer.
- Vergelijk de handtekeningen in constante tijd.
- Weiger requests waarvan
tte ver van de huidige tijd ligt. De voorbeelden staan een verschil van 5 minuten toe. - Laat Express de body als tekst inlezen, zonder die eerst te parsen.
- Geef bij Flask
request.get_data(as_text=True)door alsraw_body.
Je geheime sleutel beheren
Je team heeft één geheime sleutel die met whsec_ begint. We gebruiken die voor de webhooks van alle transcripties die je team aanmaakt.
Je kunt de sleutel bekijken of vervangen op “Webhooks”.
Een vervanging gaat direct in. Elke webhook die we daarna versturen, gebruikt de nieuwe sleutel.
Aflevering en nieuwe pogingen
- Antwoordt je server met
2xx, dan beschouwen we de webhook als afgeleverd. - Een andere statuscode, een verbindingsfout of geen antwoord binnen 10 seconden telt als een mislukte poging.
- We proberen het maximaal 6 keer in totaal. Tussen de pogingen loopt de wachttijd exponentieel op: 2, 4, 8, 16 en 32 minuten. De laatste poging is ongeveer een uur na de eerste.
- Geef meteen de uiteindelijke URL op. transcript1 volgt geen redirects; ook
3xxtelt als mislukt. - Dezelfde webhook kan vaker aankomen, bijvoorbeeld als je server pas na de time-out antwoordt. Voorkom dubbele verwerking met
data.identype.