# Bestanden uploaden

> Upload tot 95 MB met één request. Verstuur bestanden tot 2 GB in chunks en ga na een onderbreking verder waar je was.

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

Upload een audio- of videobestand om de spraak met AI te transcriberen. Kies bij uploads `auto` of `stt` voor `source`. Hoe je `language` en `diarize` instelt, lees je in [Bronnen en instellingen](https://transcript1.com/nl/docs/sources-and-options).

## Bestandsformaten en maximale grootte

Gebruik een bestandsnaam met de extensie `mp3`, `wav`, `m4a`, `flac`, `ogg`, `opus`, `mp4`, `mov`, `webm` of `mkv`. Bij een andere extensie krijg je `422 unsupported_media`. Het bestand moet ook een audiospoor hebben. Ontbreekt dat, dan mislukt de transcriptie met `unsupported_media`.

| Upload | Limiet |
|---|---|
| In één request (`multipart/form-data`) | 95 MB |
| In chunks | 2 GB (2.000.000.000 bytes), of minder als je abonnement een lagere limiet heeft. |
| Gelijktijdig openstaande uploads in chunks per team | 20 |

Is het bestand te groot, dan krijg je `413 media_too_large`. In `max_bytes` staat hoeveel bytes je maximaal mag uploaden.

## Tot 95 MB in één request

Stuur het bestand als `multipart/form-data` naar `POST /v1/transcriptions`, in het veld `file`. Tot 95 MB is één request genoeg. Je kunt ook deze formuliervelden meesturen: `language`, `diarize` (`true` of `false`), `source` (`auto` of `stt`) en `webhook_url`. Ze zijn allemaal optioneel.

```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
```

Je krijgt een transcriptieobject terug met `source.platform` op `upload`. Net als bij videolinks betekent HTTP `202` dat de transcriptie nog loopt en `200` dat er een eindstatus is. Stuur je een leeg bestand, dan krijg je `422 invalid_request`.

## Grotere bestanden in chunks uploaden

Een groot bestand verstuur je in delen, ofwel chunks. Valt de verbinding weg, dan hoef je niet opnieuw te beginnen. Zo werkt het:

### 1. Maak de upload aan

Geef de bestandsnaam en bestandsgrootte mee in JSON. Je mag ook `mime_type` opgeven. Gebruik een `Idempotency-Key`-header, zodat je dit request veilig kunt herhalen:

```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}'
```

De API antwoordt met HTTP `201`. De transcriptie heeft dan status `awaiting_upload` en bevat een `upload`-blok:

```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 }
    ]
  }
}
```

Je hoeft zelf geen chunkgrootte te kiezen. De server deelt het bestand op in chunks van ongeveer 8 MiB (8.388.608 bytes). De lijst staat in `chunks`; hierboven zie je een ingekorte versie. Houd `X-Upload-Token` geheim. Dit token geldt alleen voor deze upload en verloopt na 1 uur, op `expires_at`.

### 2. Verstuur de chunks

Vul voor elke chunk de `index` in op de plek van `{index}` in `chunk_url_template`. Stuur daar een `PUT`-request naartoe. Lees vanaf `offset` precies `size` bytes uit het bestand en stuur die als body met `application/octet-stream`. De grootte moet exact kloppen. Geef `X-Upload-Token` mee als header; je hebt hier geen `Authorization`-header nodig.

```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
```

Na elke chunk krijg je `{ "object": "upload_chunk", "index": 0, "size": 8388608 }` terug. Stuur je dezelfde chunk nog eens, dan vervangt die de vorige versie. Een verkeerde grootte of een index die niet in het uploadplan staat geeft `422 invalid_request`. Zonder geldig, niet-verlopen token krijg je `401 unauthorized`.

### 3. Ga verder na een onderbreking

Vraag met `GET /v1/transcriptions/:id/upload` op welke chunks de server al heeft:

```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"
}
```

Stuur alleen de chunks die nog niet in `received_chunks` staan. Is het token verlopen? Herhaal het request uit stap 1 met dezelfde `Idempotency-Key` en parameters. Je krijgt dezelfde transcriptie terug, maar met een nieuw uploadtoken.

### 4. Rond de upload af

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

Met `POST /v1/transcriptions/:id/upload/complete` laat je de server controleren of alle chunks er zijn en de juiste grootte hebben. Daarna voegt de server het bestand samen en zet de transcriptie in de wachtrij. Je krijgt HTTP `202` met het transcriptieobject.

Ontbreken er chunks, dan krijg je `409 upload_incomplete`. In `missing_chunks` staan de indexen die je nog moet versturen. Bij een al afgeronde of geannuleerde upload krijg je `409 invalid_state`. Volg na het afronden de status via `GET /v1/transcriptions/:id?wait=60` of wacht op een [webhook](https://transcript1.com/nl/docs/webhooks).

### Het hele uploadproces in 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"])
```

## Limieten en wat we bewaren

- Per team mogen maximaal 20 uploads tegelijk op `awaiting_upload` staan. Maak je er meer aan, dan krijg je `429 too_many_active_jobs` met een `Retry-After`-header.
- Rond je de upload niet binnen 24 uur na het aanmaken af, dan annuleren we die en verwijderen we de chunks.
- We verwijderen het mediabestand 24 uur nadat de transcriptie `completed`, `failed` of `canceled` bereikt. Het transcript blijft staan totdat je het zelf verwijdert met `DELETE /v1/transcriptions/:id`.
- Zodra de duur bekend is, berekenen we de kosten per minuut van de opname, naar boven afgerond. Mislukt de transcriptie of annuleer je die, dan betaal je niets. Lees meer over [minuten, abonnementen en limieten](https://transcript1.com/nl/docs/billing-and-limits).
