DokumentasiReferensi REST API
Referensi REST API
Cari parameter dan contoh untuk setiap endpoint /v1. Spesifikasi OpenAPI tersedia untuk diunduh dalam JSON atau YAML.
Pengantar
https://api.transcript1.com/v1Gunakan https://api.transcript1.com/v1 sebagai base URL untuk semua endpoint berikut.
- Body request dan respons memakai JSON. Nama field mengikuti
snake_case. - Timestamp mengikuti ISO 8601 dengan zona waktu UTC, contohnya
2026-10-13T09:30:00.000Z. - Durasi rekaman dan waktu pada transkrip dihitung dalam detik.
- Format error seragam untuk semua endpoint. Baca struktur error.
- Respons untuk request yang memakai autentikasi menyertakan
X-RateLimit-LimitdanX-RateLimit-Remaining. Penjelasannya ada di biaya dan batas penggunaan. - Dengan
source=auto, transcript1 mencari subtitle bahasa asli video di YouTube, X, TikTok, Bluesky, Dailymotion, dan LinkedIn terlebih dahulu. Jika tidak ada yang bisa dipakai, proses beralih ke speech to text. Tidak semua video menyediakan subtitle. Modesource=captions(hanya subtitle) tidak mengunduh audio atau memakai STT sebagai pengganti; tanpa subtitle yang bisa dipakai, transkripsi gagal denganno_captions. Opsisource=sttdan pengenalan pembicara langsung memakai audio. Baca perbandingan sumber transkrip untuk batas pengambilan subtitle serta perbedaan timestamp per segmen dan per kata.
Untuk integrasi dengan tool, unduh spesifikasi OpenAPI dalam format berikut:
Autentikasi
curl https://api.transcript1.com/v1/account \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"Autentikasi setiap request melalui header Authorization dengan format Authorization: Bearer <token>.
- Untuk akses ke semua endpoint, buat kunci API melalui halaman “API keys”. Kunci ini berawalan
t1_. - Saat Anda menghubungkan klien MCP seperti Claude atau ChatGPT, klien menerima token akses OAuth berawalan
t1o_. Aksesnya mengikuti scopetranscriptions:readdantranscriptions:write.
| Scope | Endpoint yang bisa diakses |
|---|---|
transcriptions:read | GET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload, dan GET /v1/account |
transcriptions:write | POST /v1/transcriptions, DELETE /v1/transcriptions/:id, dan POST /v1/transcriptions/:id/upload/complete |
- Jika token tidak memiliki scope yang dibutuhkan, API merespons dengan
403 insufficient_scope. - Jika kredensial belum dikirim atau tidak valid, responsnya
401 unauthorized.
Khusus pengiriman chunk lewat PUT /v1/transcriptions/:id/upload/chunks/:index, gunakan X-Upload-Token. Token diterbitkan saat sesi unggahan dibuat. Ikuti panduan unggahan untuk alur lengkapnya.
Idempotensi
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"}'Sertakan header Idempotency-Key saat memanggil POST /v1/transcriptions. Header ini menjaga agar retry tidak membuat transkripsi kedua.
Pilih nilai sepanjang 1 sampai 255 karakter dan kirim pada header, bukan query.
- Jika kunci dan parameter sama, API mengembalikan transkripsi dari request pertama tanpa biaya tambahan.
- Jika kunci sama tetapi parameternya berbeda, API menolak request dengan
409 idempotency_conflict. - Untuk unggahan bertahap, ulangi request pembuatan dengan kunci yang sama jika memerlukan token unggahan baru.
Error
{
"error": {
"code": "insufficient_minutes",
"message": "Not enough minutes",
"retryable": false,
"action_url": "https://transcript1.com/dashboard/billing",
"minutes_needed": 20,
"minutes_remaining": 3
}
}- Jika API menolak request, Anda mendapat respons error langsung.
- Jika request diterima tetapi proses transkripsi kemudian gagal, job memiliki
statusbernilaifailed.
Struktur respons error
Respons error dari /v1 selalu memuat objek error. Di dalamnya ada code, message, dan retryable.
- Jadikan
codesebagai dasar penanganan error dalam aplikasi. - Gunakan
messageuntuk menjelaskan masalah kepada pengguna. Jangan bergantung pada teks persisnya karena bisa berubah. - Jika
retryablebernilaitrue, request yang sama mungkin berhasil saat dicoba kembali nanti.
Beberapa kode juga menyertakan informasi tambahan selain retryable:
| Field | Kode error | Keterangan |
|---|---|---|
action_url | insufficient_minutes | Link untuk membeli paket atau top-up. |
minutes_needed, minutes_remaining | insufficient_minutes | Jumlah menit yang dibutuhkan request dan saldo menit Anda. Kebutuhan menit bernilai null jika belum diketahui. |
supported_platforms | unsupported_platform | Daftar nilai platform yang diterima: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"]. |
missing_chunks | upload_incomplete | Daftar indeks chunk yang belum sampai ke server. |
max_bytes | media_too_large | Batas ukuran file yang berlaku untuk Anda, dalam byte. |
params | invalid_request | Rincian validasi body atau query: setiap field dipasangkan dengan penjelasan masalahnya. |
Saat API menolak request
| Status HTTP | code | Bisa retry | Penyebab dan tindakan |
|---|---|---|---|
| 400, 422 | invalid_request | Tidak | Ada parameter atau field body yang belum diisi atau tidak valid. Contohnya diarize bersama source: "captions", atau reuse_recent bersama webhook_url. Body yang bukan JSON valid juga memakai kode ini. |
| 422 | invalid_url | Tidak | Link tidak diterima atau platform tidak sesuai dengan link. Gunakan YouTube, X, TikTok, Instagram, Facebook, Bluesky, atau platform lain dalam daftar sumber. |
| 422 | unsupported_platform | Tidak | Nilai platform tidak termasuk platform yang didukung. |
| 422 | unsupported_media | Tidak | Ekstensi file unggahan tidak didukung. |
| 401 | unauthorized | Tidak | Kredensial belum dikirim, tidak valid, sudah kedaluwarsa, atau sudah dicabut. Kode ini juga berlaku untuk token unggahan yang tidak valid atau kedaluwarsa. |
| 403 | insufficient_scope | Tidak | Token OAuth belum memiliki scope transcriptions:read atau transcriptions:write yang diperlukan. |
| 403 | email_not_verified | Tidak | Alamat email belum diverifikasi. Selesaikan verifikasi sebelum membuat transkripsi. |
| 402 | insufficient_minutes | Tidak | Saldo menit yang bisa dipakai sudah 0 atau tidak cukup untuk transkripsi ini. |
| 402 | no_active_plan | Tidak | Pembelian top-up memerlukan paket berbayar aktif. Kode ini berasal dari endpoint penagihan dasbor, bukan endpoint transkripsi. |
| 404 | not_found | Tidak | Tidak ada transkripsi dengan ID tersebut, atau transkripsi milik tim lain. |
| 409 | upload_incomplete | Tidak | Belum semua chunk diterima. Kirim bagian yang kurang, lalu ulangi langkah penyelesaian unggahan. |
| 409 | invalid_state | Tidak | Transkripsi tidak sedang menunggu unggahan. Anda tidak bisa mengirim chunk atau menyelesaikan unggahan. |
| 409 | idempotency_conflict | Tidak | Header Idempotency-Key sudah dipakai untuk parameter yang berbeda. |
| 413 | media_too_large | Tidak | File melewati batas ukuran Anda. Periksa batas unggahan. |
| 413 | invalid_request | Tidak | Body JSON melebihi 1 MB. Untuk file yang terlalu besar, kodenya media_too_large. |
| 429 | rate_limited | Ya | Jumlah request per menit melewati batas. Tunggu sesuai jumlah detik pada Retry-After. |
| 429 | too_many_active_jobs | Ya | Terlalu banyak transkripsi berjalan atau sesi unggahan terbuka. Tunggu sesuai jumlah detik pada Retry-After. |
| 500 | internal_error | Ya | Ada masalah pada sistem kami. Coba kembali beberapa saat lagi. |
| 503 | service_unavailable | Ya | transcript1 sementara tidak bisa diakses. Coba kembali beberapa saat lagi. |
Jika browser memakai cookie login sebagai autentikasi, API juga memeriksa origin request. Akses dari situs lain mendapat 403 forbidden_origin.
Pemeriksaan origin ini tidak berlaku untuk request API yang memakai header Authorization.
Semua respons 429 menyertakan header Retry-After dalam detik. Tunggu sesuai nilainya sebelum mencoba lagi. Rincian batasnya ada di biaya dan batas penggunaan.
Tentukan kapan perlu retry
- Retry hanya untuk
retryablebernilaitrue:rate_limited,too_many_active_jobs,internal_error, danservice_unavailable. - Jika ada
Retry-After, tunggu sesuai nilainya. Jika tidak, tambah jeda pada setiap percobaan. - Saat mengulang
POST /v1/transcriptions, pakai headerIdempotency-Keyyang sama. Dengan begitu, Anda tidak terkena biaya kedua jika request pertama ternyata sudah berhasil. - Untuk kode lain, perbaiki request terlebih dahulu sebelum mengirimnya kembali.
Saat proses transkripsi gagal
Jika proses transkripsi gagal, status berubah menjadi failed. Baca penyebabnya dari code dan message dalam objek error.
Job bisa gagal setelah API mengirim respons awal. Karena itu, periksa error saat polling atau ketika menerima webhook transcription.failed.
error.code | Penyebab | Langkah berikutnya |
|---|---|---|
media_unavailable | Video tidak ditemukan atau sudah tidak tersedia. | Jangan langsung retry. Periksa URL. |
media_private | Video hanya bisa diakses secara privat. | Gunakan postingan publik, atau video YouTube berstatus publik maupun unlisted. |
login_required | Platform meminta Anda login untuk mengakses video. | Tidak ada retry untuk kegagalan permanen ini. |
no_audio | Video tidak memiliki track audio. | Tidak ada retry untuk kegagalan permanen ini. |
age_restricted | Video memiliki batasan usia. | Unggah file rekamannya, bukan mengulang link yang sama. |
live_stream | Siaran masih berlangsung atau belum dimulai. | Coba lagi setelah siaran berakhir. |
no_captions | Mode captions pada source tidak menemukan track subtitle yang bisa dipakai. | Ubah ke source: "auto" atau "stt". |
media_too_long | Durasi melebihi batas speech to text, yaitu 5 jam. | Bagi rekaman menjadi beberapa bagian sebelum mengirimnya lagi. |
unsupported_media | File tidak bisa dibaca atau tidak memiliki track audio. | Kirim file dengan format yang didukung. |
no_speech | Tidak ada ucapan yang terdeteksi. | Jangan retry. |
unsupported_language | Bahasa dalam rekaman tidak didukung. | Ubah language ke kode yang didukung atau "auto" sebelum mencoba lagi. |
insufficient_minutes | Saldo menit habis sebelum transkripsi bisa ditagih. | Isi saldo dengan top-up, lalu coba lagi. |
fetch_failed | Media tetap gagal diambil setelah beberapa percobaan. | Boleh dicoba lagi nanti. |
transcription_failed | Speech to text tetap gagal setelah beberapa percobaan. | Boleh dicoba lagi nanti. |
Jika transkripsi gagal atau dibatalkan, Anda tidak membayar. Menit yang sudah dipotong untuk job tersebut dikembalikan ke saldo.
Objek 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."
}
]
}
}Atribut
- idstringwajib
- objectstringwajib
Pilihan nilai
transcription - statusstringwajib
Pilihan nilai
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - sourceobjectwajib
Tampilkan atribut turunanSembunyikan atribut turunan
With
url- platformstringwajib
- urlstringwajibboleh null
- media_idstringwajibboleh null
With
filename- platformstringwajib
Pilihan nilai
upload - filenamestringwajibboleh null
- sizenumberwajibboleh null
- mediaobjectwajibboleh null
Tampilkan atribut turunanSembunyikan atribut turunan
- titlestringopsionalboleh null
- authorstringopsionalboleh null
- author_urlstringopsionalboleh null
- thumbnail_urlstringopsionalboleh null
- filenamestringopsional
- mime_typestringopsional
- sizenumberopsional
- duration_secondsnumberopsionalboleh null
- optionsobjectwajib
Tampilkan atribut turunanSembunyikan atribut turunan
- sourcestringwajib
Pilihan nilai
autocaptionsstt - languagestringwajib
- diarizebooleanwajib
- transcript_sourcestringwajibboleh null
Pilihan nilai
youtube_captionsx_captionsspeech_to_text - caption_kindstringwajibboleh null
Pilihan nilai
manualauto_generated - languagestringwajibboleh null
- billed_minutesintegerwajibboleh null
- cachedbooleanwajib
- errorobjectwajibboleh null
Tampilkan atribut turunanSembunyikan atribut turunan
- codestringwajib
- messagestringwajibboleh null
- created_atstring · date-timewajibboleh null
- completed_atstring · date-timewajibboleh null
- deleted_atstring · date-timewajibboleh null
When the transcription was deleted. Always
nullunless the request includesinclude_deleted=true. - transcriptobjectopsional
Included in
GETresponses for completed transcriptions.Tampilkan atribut turunanSembunyikan atribut turunan
- textstringwajib
- speakersarray of stringswajib
- segmentsarray of objectswajib
Tampilkan atribut turunanSembunyikan atribut turunan
- startnumberwajib
- endnumberwajib
- speakerstringwajibboleh null
- languagestringwajibboleh null
- textstringwajib
- wordsarray of objectsopsionalboleh null
Tampilkan atribut turunanSembunyikan atribut turunan
- startnumberwajib
- endnumberwajib
- textstringwajib
- confidencenumberopsionalboleh null
- speakerstringwajibboleh null
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
- waitintegeropsionalboleh nullDefault: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- reuse_recentstringopsionalDefault: 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.Pilihan nilai
truefalse
Headers
- Idempotency-Keystringopsional
Returns the original transcription when you repeat a request with the same key and parameters.
Body
With url
- platformstringopsional
Optional; detected from the link. If supplied, must match the link.
Pilihan nilai
youtubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat - urlstringwajib
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.
- sourcestringopsional
"auto" uses YouTube or X captions when available, otherwise speech-to-text. "captions" is YouTube/X-only. "stt" always uses speech-to-text.
Pilihan nilai
autocaptionsstt - languagestringopsional
- diarizebooleanopsional
- webhook_urlstring · uriopsional
With upload
- uploadobjectwajib
Tampilkan atribut turunanSembunyikan atribut turunan
- filenamestringwajib
- sizeintegerwajib
- mime_typestringopsional
- sourcestringopsional
Pilihan nilai
autostt - languagestringopsional
- diarizebooleanopsional
- webhook_urlstring · uriopsional
multipart/form-data
- filestring · binarywajib
- sourcestringopsional
Pilihan nilai
autostt - languagestringopsional
- diarizebooleanopsional
- webhook_urlstring · uriopsional
Respons
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
Status lain
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
- idstringwajib
Query
- waitintegeropsionalboleh nullDefault: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- formatstringopsionalDefault: json
Pilihan nilai
jsontextsrtvtt - includestringopsional
Pilihan nilai
words - timestampsstringopsionalDefault: false
Pilihan nilai
truefalse - include_deletedstringopsionalDefault: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Pilihan nilai
truefalse
Respons
200 OK— The transcription, or the rendered transcript202 Accepted— A rendered format was requested while the transcription is still processing
Status lain
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
- limitintegeropsionalDefault: 20
- cursorstringopsional
- statusstringopsional
Pilihan nilai
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - platformstringopsional
- include_deletedstringopsionalDefault: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Pilihan nilai
truefalse
Respons
200 OK— A page of transcriptions
Status lain
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"Respons tanpa body
Path
- idstringwajib
Respons
204 No Content— Canceled or deleted
Status lain
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
- idstringwajib
- indexintegeropsionalboleh null
Zero-based chunk number from the upload plan
Headers
- X-Upload-Tokenstringwajib
Body
- (raw body)string · binarywajib
Respons
200 OK— The chunk was stored
Status lain
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
- idstringwajib
Respons
200 OK— Upload progress
Status lain
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
- idstringwajib
Respons
202 Accepted— The transcription is queued
Status lain
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
}
}Respons
200 OK— The authenticated user's account
Status lain
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.
Objek 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
}
}Atribut
- objectstringwajib
Pilihan nilai
account - userobjectwajib
Tampilkan atribut turunanSembunyikan atribut turunan
- idstringwajib
- emailstringwajib
- planobjectwajibboleh null
Tampilkan atribut turunanSembunyikan atribut turunan
- idstringwajib
- namestringwajib
- minutesobjectwajib
Tampilkan atribut turunanSembunyikan atribut turunan
- remainingnumberwajib
- cyclenumberwajib
- topupnumberwajib
- topup_usablebooleanwajib
- resets_atstring · date-timewajibboleh null
- renews_atstring · date-timewajibboleh null
- limitsobjectwajib
Tampilkan atribut turunanSembunyikan atribut turunan
- requests_per_minutenumberwajib
- max_active_jobsnumberwajib
Webhook
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
}
}Tentukan webhook_url saat membuat transkripsi. Setelah proses selesai atau gagal, transcript1 mengirim notifikasi lewat POST ke alamat tersebut. Setiap pengiriman dilengkapi tanda tangan untuk Anda verifikasi.
Webhook tersedia lewat REST API, bukan lewat server MCP. Tool MCP tidak menerima parameter webhook.
Tentukan alamat penerima
Letakkan webhook_url pada request POST /v1/transcriptions sesuai jenis input:
- Untuk link video yang didukung atau saat menyiapkan unggahan bertahap, isi pada body JSON.
- Jika file dikirim dalam satu request, isi sebagai field form.
Pastikan URL memenuhi ketentuan berikut:
- Protokolnya HTTPS.
- Tidak menyertakan nama pengguna atau kata sandi.
- Hasil resolusinya berupa alamat publik.
Alamat privat, loopback, link-local, dan alamat nonpublik lain ditolak dengan 422 invalid_request. transcript1 memeriksa alamat saat transkripsi dibuat dan sebelum setiap pengiriman webhook.
Jangan gunakan webhook_url bersama reuse_recent=true; keduanya tidak bisa digabungkan.
Kapan webhook dikirim
type | Pemicu |
|---|---|
transcription.completed | Status transkripsi berubah menjadi completed. |
transcription.failed | Status transkripsi berubah menjadi failed. Baca code dan message di data.error. |
Jika transkripsi dibatalkan, tidak ada webhook yang dikirim.
Isi notifikasi
Body webhook memakai JSON dengan informasi berikut:
- Field
created_atmencatat kapan event dibuat. - Blok
datamemuat objek transkripsi, tetapi tidak menyertakantranscript.
Ambil teks transkrip lewat GET /v1/transcriptions/:id. Gunakan id yang tercantum di data.
Setiap request webhook membawa header berikut:
Content-Type: application/jsonUser-Agent: transcript1-webhooks/1.0X-Transcript1-Signature, untuk verifikasi berikut.
Periksa tanda tangan sebelum membaca JSON
Baca header X-Transcript1-Signature pada setiap pengiriman. Format nilainya t=<unix seconds>,v1=<hex digest>.
tmencatat waktu penandatanganan request sebagai Unix time dalam detik.v1berisi HMAC-SHA256 dalam format heksadesimal. Hitung dari"<t>.<raw body>": gabungkan nilait, titik, lalu body request persis seperti yang diterima. Gunakan signing secret lengkap sebagai kunci, termasuk awalanwhsec_.
Verifikasi byte body asli sebelum melakukan parse JSON. Parse lalu serialisasi ulang bisa mengubah spasi atau urutan key, sehingga tanda tangan tidak lagi cocok.
- Gunakan metode perbandingan constant-time untuk tanda tangan.
- Tolak request jika nilai
tterlalu jauh dari waktu sekarang. Contoh kode memakai toleransi 5 menit. - Di Express, baca body sebagai teks mentah agar JSON belum diparse saat verifikasi.
- Di Flask, teruskan
request.get_data(as_text=True)sebagairaw_body.
Kelola signing secret tim
Signing secret berawalan whsec_ berlaku untuk seluruh tim. Semua webhook dari transkripsi tim memakai secret yang sama.
Untuk melihat atau menggantinya, buka halaman “Webhooks”.
Penggantian berlaku saat itu juga. Semua webhook yang dikirim setelahnya memakai secret baru.
Respons, retry, dan notifikasi ganda
- Balas dengan status
2xxagar pengiriman dianggap berhasil. - Kode status lain, error koneksi, atau tidak ada respons dalam 10 detik dihitung sebagai percobaan gagal.
- Jika gagal, transcript1 mengirim ulang dengan jeda yang makin panjang (exponential backoff), maksimal 6 percobaan termasuk pengiriman pertama. Jedanya 2, 4, 8, 16, dan 32 menit. Percobaan terakhir berlangsung sekitar satu jam setelah yang pertama.
- Berikan URL tujuan akhir. transcript1 tidak mengikuti redirect; respons
3xxdihitung gagal. - Notifikasi yang sama bisa masuk lebih dari sekali, misalnya karena server Anda baru merespons setelah timeout. Periksa kombinasi
data.iddantypeuntuk mengabaikan duplikat.