DokumentasiReferensi REST API

Referensi REST API

Cari parameter dan contoh untuk setiap endpoint /v1. Spesifikasi OpenAPI tersedia untuk diunduh dalam JSON atau YAML.

Pengantar

Base URL
https://api.transcript1.com/v1

Gunakan 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-Limit dan X-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. Mode source=captions (hanya subtitle) tidak mengunduh audio atau memakai STT sebagai pengganti; tanpa subtitle yang bisa dipakai, transkripsi gagal dengan no_captions. Opsi source=stt dan 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 scope transcriptions:read dan transcriptions:write.
ScopeEndpoint yang bisa diakses
transcriptions:readGET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload, dan GET /v1/account
transcriptions:writePOST /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

402 Payment Required
{
  "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 status bernilai failed.

Struktur respons error

Respons error dari /v1 selalu memuat objek error. Di dalamnya ada code, message, dan retryable.

  • Jadikan code sebagai dasar penanganan error dalam aplikasi.
  • Gunakan message untuk menjelaskan masalah kepada pengguna. Jangan bergantung pada teks persisnya karena bisa berubah.
  • Jika retryable bernilai true, request yang sama mungkin berhasil saat dicoba kembali nanti.

Beberapa kode juga menyertakan informasi tambahan selain retryable:

FieldKode errorKeterangan
action_urlinsufficient_minutesLink untuk membeli paket atau top-up.
minutes_needed, minutes_remaininginsufficient_minutesJumlah menit yang dibutuhkan request dan saldo menit Anda. Kebutuhan menit bernilai null jika belum diketahui.
supported_platformsunsupported_platformDaftar nilai platform yang diterima: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"].
missing_chunksupload_incompleteDaftar indeks chunk yang belum sampai ke server.
max_bytesmedia_too_largeBatas ukuran file yang berlaku untuk Anda, dalam byte.
paramsinvalid_requestRincian validasi body atau query: setiap field dipasangkan dengan penjelasan masalahnya.

Saat API menolak request

Status HTTPcodeBisa retryPenyebab dan tindakan
400, 422invalid_requestTidakAda 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.
422invalid_urlTidakLink tidak diterima atau platform tidak sesuai dengan link. Gunakan YouTube, X, TikTok, Instagram, Facebook, Bluesky, atau platform lain dalam daftar sumber.
422unsupported_platformTidakNilai platform tidak termasuk platform yang didukung.
422unsupported_mediaTidakEkstensi file unggahan tidak didukung.
401unauthorizedTidakKredensial belum dikirim, tidak valid, sudah kedaluwarsa, atau sudah dicabut. Kode ini juga berlaku untuk token unggahan yang tidak valid atau kedaluwarsa.
403insufficient_scopeTidakToken OAuth belum memiliki scope transcriptions:read atau transcriptions:write yang diperlukan.
403email_not_verifiedTidakAlamat email belum diverifikasi. Selesaikan verifikasi sebelum membuat transkripsi.
402insufficient_minutesTidakSaldo menit yang bisa dipakai sudah 0 atau tidak cukup untuk transkripsi ini.
402no_active_planTidakPembelian top-up memerlukan paket berbayar aktif. Kode ini berasal dari endpoint penagihan dasbor, bukan endpoint transkripsi.
404not_foundTidakTidak ada transkripsi dengan ID tersebut, atau transkripsi milik tim lain.
409upload_incompleteTidakBelum semua chunk diterima. Kirim bagian yang kurang, lalu ulangi langkah penyelesaian unggahan.
409invalid_stateTidakTranskripsi tidak sedang menunggu unggahan. Anda tidak bisa mengirim chunk atau menyelesaikan unggahan.
409idempotency_conflictTidakHeader Idempotency-Key sudah dipakai untuk parameter yang berbeda.
413media_too_largeTidakFile melewati batas ukuran Anda. Periksa batas unggahan.
413invalid_requestTidakBody JSON melebihi 1 MB. Untuk file yang terlalu besar, kodenya media_too_large.
429rate_limitedYaJumlah request per menit melewati batas. Tunggu sesuai jumlah detik pada Retry-After.
429too_many_active_jobsYaTerlalu banyak transkripsi berjalan atau sesi unggahan terbuka. Tunggu sesuai jumlah detik pada Retry-After.
500internal_errorYaAda masalah pada sistem kami. Coba kembali beberapa saat lagi.
503service_unavailableYatranscript1 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 retryable bernilai true: rate_limited, too_many_active_jobs, internal_error, dan service_unavailable.
  • Jika ada Retry-After, tunggu sesuai nilainya. Jika tidak, tambah jeda pada setiap percobaan.
  • Saat mengulang POST /v1/transcriptions, pakai header Idempotency-Key yang 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.codePenyebabLangkah berikutnya
media_unavailableVideo tidak ditemukan atau sudah tidak tersedia.Jangan langsung retry. Periksa URL.
media_privateVideo hanya bisa diakses secara privat.Gunakan postingan publik, atau video YouTube berstatus publik maupun unlisted.
login_requiredPlatform meminta Anda login untuk mengakses video.Tidak ada retry untuk kegagalan permanen ini.
no_audioVideo tidak memiliki track audio.Tidak ada retry untuk kegagalan permanen ini.
age_restrictedVideo memiliki batasan usia.Unggah file rekamannya, bukan mengulang link yang sama.
live_streamSiaran masih berlangsung atau belum dimulai.Coba lagi setelah siaran berakhir.
no_captionsMode captions pada source tidak menemukan track subtitle yang bisa dipakai.Ubah ke source: "auto" atau "stt".
media_too_longDurasi melebihi batas speech to text, yaitu 5 jam.Bagi rekaman menjadi beberapa bagian sebelum mengirimnya lagi.
unsupported_mediaFile tidak bisa dibaca atau tidak memiliki track audio.Kirim file dengan format yang didukung.
no_speechTidak ada ucapan yang terdeteksi.Jangan retry.
unsupported_languageBahasa dalam rekaman tidak didukung.Ubah language ke kode yang didukung atau "auto" sebelum mencoba lagi.
insufficient_minutesSaldo menit habis sebelum transkripsi bisa ditagih.Isi saldo dengan top-up, lalu coba lagi.
fetch_failedMedia tetap gagal diambil setelah beberapa percobaan.Boleh dicoba lagi nanti.
transcription_failedSpeech 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

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 nilaitranscription

  • statusstringwajib

    Pilihan nilaiawaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled

  • sourceobjectwajib
    Tampilkan atribut turunan
    With url
    • platformstringwajib
    • urlstringwajibboleh null
    • media_idstringwajibboleh null
    With filename
    • platformstringwajib

      Pilihan nilaiupload

    • filenamestringwajibboleh null
    • sizenumberwajibboleh null
  • mediaobjectwajibboleh null
    Tampilkan atribut turunan
    • titlestringopsionalboleh null
    • authorstringopsionalboleh null
    • author_urlstringopsionalboleh null
    • thumbnail_urlstringopsionalboleh null
    • filenamestringopsional
    • mime_typestringopsional
    • sizenumberopsional
    • duration_secondsnumberopsionalboleh null
  • optionsobjectwajib
    Tampilkan atribut turunan
    • sourcestringwajib

      Pilihan nilaiautocaptionsstt

    • languagestringwajib
    • diarizebooleanwajib
  • transcript_sourcestringwajibboleh null

    Pilihan nilaiyoutube_captionsx_captionsspeech_to_text

  • caption_kindstringwajibboleh null

    Pilihan nilaimanualauto_generated

  • languagestringwajibboleh null
  • billed_minutesintegerwajibboleh null
  • cachedbooleanwajib
  • errorobjectwajibboleh null
    Tampilkan 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 null unless the request includes include_deleted=true.

  • transcriptobjectopsional

    Included in GET responses for completed transcriptions.

    Tampilkan atribut turunan
    • textstringwajib
    • speakersarray of stringswajib
    • segmentsarray of objectswajib
      Tampilkan atribut turunan
      • startnumberwajib
      • endnumberwajib
      • speakerstringwajibboleh null
      • languagestringwajibboleh null
      • textstringwajib
      • wordsarray of objectsopsionalboleh null
        Tampilkan 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/transcriptions

Transcribes 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 same source, language and diarize. It must have been created in the last 24 hours and must not be failed or canceled. Reuses that transcription without creating or charging for a new one.

    Pilihan nilaitruefalse

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 nilaiyoutubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat

  • 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 nilaiautocaptionsstt

  • languagestringopsional
  • diarizebooleanopsional
  • webhook_urlstring · uriopsional
With upload
  • uploadobjectwajib
    Tampilkan atribut turunan
    • filenamestringwajib
    • sizeintegerwajib
    • mime_typestringopsional
  • sourcestringopsional

    Pilihan nilaiautostt

  • languagestringopsional
  • diarizebooleanopsional
  • webhook_urlstring · uriopsional
multipart/form-data
  • filestring · binarywajib
  • sourcestringopsional

    Pilihan nilaiautostt

  • languagestringopsional
  • diarizebooleanopsional
  • webhook_urlstring · uriopsional

Respons

  • 200 OK — The transcription reached a final status
  • 201 Created — The transcription is awaiting a chunked upload. upload describes where to send each chunk.
  • 202 Accepted — The transcription is processing

Status lain

  • 400 Bad Request — The JSON request body is malformed
  • 401 Unauthorized — Missing or invalid credential
  • 402 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 plan
  • 409 Conflict — Idempotency-Key was reused with different parameters.
  • 413 Content Too Large — The upload or the JSON request body is too large
  • 422 Unprocessable Content — Invalid request, URL, platform or file type
  • 429 Too Many Requests — Too many requests or too many active transcriptions. Wait for the number of seconds in Retry-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 nilaijsontextsrtvtt

  • includestringopsional

    Pilihan nilaiwords

  • timestampsstringopsionalDefault: false

    Pilihan nilaitruefalse

  • include_deletedstringopsionalDefault: false

    Also returns deleted transcriptions as read-only records without a transcript. Their deleted_at is set. Defaults to false.

    Pilihan nilaitruefalse

Respons

  • 200 OK — The transcription, or the rendered transcript
  • 202 Accepted — A rendered format was requested while the transcription is still processing

Status lain

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — Transcription not found
  • 422 Unprocessable Content — Invalid query parameters
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

List transcriptions

GET /v1/transcriptions

Lists 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 nilaiawaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled

  • platformstringopsional
  • include_deletedstringopsionalDefault: false

    Also returns deleted transcriptions as read-only records without a transcript. Their deleted_at is set. Defaults to false.

    Pilihan nilaitruefalse

Respons

  • 200 OK — A page of transcriptions

Status lain

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 422 Unprocessable Content — Invalid query parameters or cursor
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-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 credential
  • 403 Forbidden — Insufficient scope, cross-site request or a single-member team plan
  • 404 Not Found — Transcription not found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-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 token
  • 404 Not Found — Transcription not found
  • 409 Conflict — The transcription is no longer awaiting an upload
  • 422 Unprocessable Content — Index outside the chunk plan or wrong body size

Get upload progress

GET /v1/transcriptions/{id}/upload

Lists 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 credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — Transcription or upload not found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Complete a chunked upload

POST /v1/transcriptions/{id}/upload/complete

Checks 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 credential
  • 402 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 plan
  • 404 Not Found — Transcription or upload not found
  • 409 Conflict — upload_incomplete means chunks are missing. missing_chunks lists them. invalid_state means 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 in Retry-After.

Account

Your plan, remaining minutes and rate limits.

Get your account

GET /v1/account

Returns 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 credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — No account found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Objek Account

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 nilaiaccount

  • userobjectwajib
    Tampilkan atribut turunan
    • idstringwajib
    • emailstringwajib
  • planobjectwajibboleh null
    Tampilkan atribut turunan
    • idstringwajib
    • namestringwajib
  • minutesobjectwajib
    Tampilkan atribut turunan
    • remainingnumberwajib
    • cyclenumberwajib
    • topupnumberwajib
    • topup_usablebooleanwajib
    • resets_atstring · date-timewajibboleh null
  • renews_atstring · date-timewajibboleh null
  • limitsobjectwajib
    Tampilkan 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));
};
transcription.completed
{
  "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

typePemicu
transcription.completedStatus transkripsi berubah menjadi completed.
transcription.failedStatus 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_at mencatat kapan event dibuat.
  • Blok data memuat objek transkripsi, tetapi tidak menyertakan transcript.

Ambil teks transkrip lewat GET /v1/transcriptions/:id. Gunakan id yang tercantum di data.

Setiap request webhook membawa header berikut:

  • Content-Type: application/json
  • User-Agent: transcript1-webhooks/1.0
  • X-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>.

  • t mencatat waktu penandatanganan request sebagai Unix time dalam detik.
  • v1 berisi HMAC-SHA256 dalam format heksadesimal. Hitung dari "<t>.<raw body>": gabungkan nilai t, titik, lalu body request persis seperti yang diterima. Gunakan signing secret lengkap sebagai kunci, termasuk awalan whsec_.

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 t terlalu 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) sebagai raw_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 2xx agar 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 3xx dihitung gagal.
  • Notifikasi yang sama bisa masuk lebih dari sekali, misalnya karena server Anda baru merespons setelah timeout. Periksa kombinasi data.id dan type untuk mengabaikan duplikat.