# Mengunggah file

> Unggah file hingga 95 MB dalam satu request. Untuk file hingga 2 GB, kirim per chunk dan lanjutkan jika koneksi terputus.

Canonical: https://transcript1.com/id/docs/uploads
Language: id
Last updated: 2026-10-14

File audio dan video yang Anda unggah diproses dengan transkripsi AI. Gunakan `source` bernilai `auto` atau `stt`. Jika ingin menentukan `language` atau mengaktifkan `diarize`, baca [sumber dan pengaturan transkripsi](https://transcript1.com/id/docs/sources-and-options).

## Format file dan ukurannya

Pastikan nama file memakai ekstensi `mp3`, `wav`, `m4a`, `flac`, `ogg`, `opus`, `mp4`, `mov`, `webm`, atau `mkv`. API menolak ekstensi lain dengan `422 unsupported_media`. Rekaman juga harus memiliki track audio. Jika tidak ada, transkripsi gagal dengan `unsupported_media`.

| Jenis batas | Maksimal |
|---|---|
| Satu request (`multipart/form-data`) | 95 MB |
| Unggahan bertahap per chunk | 2 GB (2.000.000.000 byte). Paket Anda bisa memiliki batas lebih rendah. |
| Sesi unggahan bertahap yang masih terbuka per tim | 20 |

Jika ukuran file melewati batas, API mengirim `413 media_too_large`. Field `max_bytes` pada error menunjukkan batas ukuran yang berlaku.

## Kirim file dalam satu request

Kirim file hingga 95 MB ke `POST /v1/transcriptions` sebagai `multipart/form-data`. Gunakan nama part `file`. Anda juga boleh menambahkan field form `language`, `diarize` (`true` atau `false`), `source` (`auto` atau `stt`), dan `webhook_url`.

```bash
curl -X POST "https://api.transcript1.com/v1/transcriptions?wait=60" \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -F file=@interview.m4a \
  -F language=en \
  -F diarize=true
```

API mengirim objek transkripsi dengan `source.platform` bernilai `upload`. Seperti saat mengirim link video, kode HTTP-nya `202` selama proses berjalan dan `200` setelah proses berakhir. Jangan kirim file kosong; API akan menolaknya dengan `422 invalid_request`.

## Unggah file besar per chunk

Untuk file besar, kirim data secara bertahap dalam beberapa chunk. Anda bisa melanjutkan dari bagian yang belum terkirim jika koneksi terputus. Prosesnya terdiri dari empat langkah berikut.

### 1. Siapkan sesi unggahan

Mulai dengan mengirim nama dan ukuran file dalam JSON. Isi `mime_type` jika perlu. Sertakan header `Idempotency-Key` agar Anda bisa mengulang request ini tanpa membuat transkripsi baru:

```bash
curl -X POST "https://api.transcript1.com/v1/transcriptions" \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Idempotency-Key: interview-2026-10-13" \
  -H "Content-Type: application/json" \
  -d '{"upload":{"filename":"interview.m4a","size":48210334,"mime_type":"audio/mp4"},"language":"auto","diarize":true}'
```

API merespons dengan `201`. Objeknya memiliki status `awaiting_upload` dan blok `upload`:

```json
{
  "id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
  "object": "transcription",
  "status": "awaiting_upload",
  "upload": {
    "chunk_url_template": "https://api.transcript1.com/v1/transcriptions/tr_8fK2Lm0PqRsTuVwXyZaBcD/upload/chunks/{index}",
    "headers": { "X-Upload-Token": "t1u_..." },
    "expires_at": "2026-10-13T10:30:00.000Z",
    "chunks": [
      { "index": 0, "offset": 0, "size": 8388608 },
      { "index": 1, "offset": 8388608, "size": 8388608 }
    ]
  }
}
```

Server menentukan pembagian chunk, masing-masing sekitar 8 MiB (8.388.608 byte). Daftarnya tersedia di `chunks`; contoh di atas disingkat. Jaga kerahasiaan `X-Upload-Token`. Token ini hanya berlaku untuk unggahan tersebut selama 1 jam, sampai waktu pada `expires_at`.

### 2. Kirim chunk sesuai daftar

Untuk setiap chunk, ganti `{index}` pada `chunk_url_template` dengan nilai `index`, lalu kirim lewat `PUT`. Baca data file mulai dari `offset` sebanyak `size` byte dan kirim sebagai `application/octet-stream`. Ukurannya harus persis sesuai daftar server. Gunakan header `X-Upload-Token` untuk autentikasi, bukan `Authorization`.

```bash
curl -X PUT "https://api.transcript1.com/v1/transcriptions/tr_8fK2Lm0PqRsTuVwXyZaBcD/upload/chunks/0" \
  -H "X-Upload-Token: t1u_..." \
  -H "Content-Type: application/octet-stream" \
  --data-binary @chunk-0.bin
```

Setelah menerima chunk, API merespons dengan `{ "object": "upload_chunk", "index": 0, "size": 8388608 }`. Jika Anda mengirim ulang chunk yang sama, data sebelumnya diganti. Ukuran yang tidak sesuai atau indeks di luar daftar mendapat `422 invalid_request`. Tanpa token yang masih berlaku, responsnya `401 unauthorized`.

### 3. Lanjutkan unggahan yang terputus

Panggil `GET /v1/transcriptions/:id/upload` untuk memeriksa data yang sudah diterima server:

```json
{
  "object": "upload",
  "status": "awaiting_upload",
  "total_bytes": 48210334,
  "uploaded_bytes": 16777216,
  "received_chunks": [0, 1],
  "chunks": [ { "index": 0, "offset": 0, "size": 8388608 } ],
  "expires_at": "2026-10-13T10:30:00.000Z"
}
```

Periksa `received_chunks`, lalu kirim hanya chunk yang belum tercantum. Jika token sudah kedaluwarsa, ulangi request pada langkah 1 dengan `Idempotency-Key` dan parameter yang sama. Anda akan mendapat transkripsi yang sama beserta token unggahan baru.

### 4. Gabungkan file dan mulai transkripsi

```bash
curl -X POST "https://api.transcript1.com/v1/transcriptions/tr_8fK2Lm0PqRsTuVwXyZaBcD/upload/complete" \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
```

Endpoint `POST /v1/transcriptions/:id/upload/complete` memeriksa kelengkapan dan ukuran semua chunk. Setelah itu, server menggabungkan file dan memasukkan transkripsi ke antrean. Responsnya `202` beserta objek transkripsi.

Jika ada chunk yang belum diterima, API mengirim `409 upload_incomplete`. Kirim ulang chunk dengan indeks yang tercantum di `missing_chunks`. Jika unggahan sudah selesai atau dibatalkan, responsnya `409 invalid_state`. Untuk mengikuti proses transkripsi, lakukan polling lewat `GET /v1/transcriptions/:id?wait=60` atau gunakan [webhook](https://transcript1.com/id/docs/webhooks).

### Jalankan seluruh alur dengan Python

```python
import os
import requests

API = "https://api.transcript1.com"
auth = {"Authorization": f"Bearer {os.environ['TRANSCRIPT1_API_KEY']}"}
path = "interview.m4a"

created = requests.post(
    f"{API}/v1/transcriptions",
    headers={**auth, "Idempotency-Key": "interview-2026-10-13"},
    json={"upload": {"filename": os.path.basename(path), "size": os.path.getsize(path)}},
)
created.raise_for_status()
transcription = created.json()
upload = transcription["upload"]

with open(path, "rb") as file:
    for chunk in upload["chunks"]:
        file.seek(chunk["offset"])
        url = upload["chunk_url_template"].replace("{index}", str(chunk["index"]))
        response = requests.put(
            url,
            data=file.read(chunk["size"]),
            headers={**upload["headers"], "Content-Type": "application/octet-stream"},
        )
        response.raise_for_status()

done = requests.post(f"{API}/v1/transcriptions/{transcription['id']}/upload/complete", headers=auth)
done.raise_for_status()
print(done.json()["status"])
```

## Batas unggahan dan masa penyimpanan

- Satu tim bisa membuka maksimal 20 sesi unggahan berstatus `awaiting_upload` sekaligus. Jika menambah sesi di atas batas itu, API mengirim `429 too_many_active_jobs` dengan header `Retry-After`.
- Selesaikan unggahan dalam 24 jam sejak sesi dibuat. Setelah itu, sesi dibatalkan dan semua chunk dihapus.
- File media dihapus 24 jam setelah status transkripsi menjadi `completed`, `failed`, atau `canceled`. Transkrip tetap tersimpan sampai Anda menghapusnya lewat `DELETE /v1/transcriptions/:id`.
- Jika transkripsi gagal atau dibatalkan, Anda tidak membayar. Penghitungan biaya dilakukan setelah durasi file diketahui: per menit rekaman, dibulatkan ke atas. Baca [biaya dan batas penggunaan](https://transcript1.com/id/docs/billing-and-limits).
