# Menangani error

> Pahami respons error dan kegagalan transkripsi, lalu tentukan kapan request perlu diperbaiki atau dicoba ulang.

Canonical: https://transcript1.com/id/docs/errors
Language: id
Last updated: 2026-10-19

Bedakan request yang ditolak API dengan transkripsi yang gagal setelah diterima. Pada kasus pertama, Anda mendapat respons error langsung. Pada kasus kedua, job sudah dibuat, tetapi akhirnya memiliki `status` bernilai `failed`.

## Struktur respons error

Jika request ke `/v1` ditolak, respons error mengikuti format berikut:

```json
{
  "error": {
    "code": "insufficient_minutes",
    "message": "Not enough minutes",
    "retryable": false,
    "action_url": "https://transcript1.com/dashboard/billing",
    "minutes_needed": 20,
    "minutes_remaining": 3
  }
}
```

Gunakan `code` untuk menentukan penanganan di aplikasi. Teks `message` boleh ditampilkan kepada pengguna, tetapi jangan jadikan teksnya patokan karena bisa berubah. Nilai `retryable: true` menandakan request yang sama mungkin berhasil jika dicoba lagi nanti.

Selain `retryable`, beberapa error menyertakan informasi tambahan:

| 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](https://transcript1.com/id/docs/sources-and-options). |
| 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](https://transcript1.com/id/docs/uploads). |
| 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 ini tidak berlaku untuk request API yang memakai header `Authorization` dengan kredensial bearer.

Semua respons `429` menyertakan `Retry-After` dalam detik. Rincian batasnya ada di [biaya dan batas penggunaan](https://transcript1.com/id/docs/billing-and-limits).

## 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. Mengirim request yang sama tanpa perubahan tidak menyelesaikan masalahnya.

## Saat proses transkripsi gagal

Job bisa gagal setelah API menerima request dan mengirim respons. Jika `status` berubah menjadi `failed`, objek transkripsi memuat `error: { "code", "message" }`. Periksa `error` saat polling atau ketika menerima [webhook](https://transcript1.com/id/docs/webhooks) `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.
