> ## Documentation Index
> Fetch the complete documentation index at: https://aircaps.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch transcription

> Transcribe recordings with A5S Async v1.

## 1. Provide the audio

Choose one:

| Source | How | Size |
| - | - | - |
| A URL | `audio_url` in the create request. Public or presigned http(s) URL. | ≤ 5 GB |
| Upload URL | [`POST /v1/files/upload-url`](https://aircaps.ai/docs/api-reference/upload-url), then `PUT` the bytes to the returned `upload_url` within an hour. Use the returned `id` as `file_id`. | ≤ 5 GB |
| Direct upload | [`POST /v1/files`](https://aircaps.ai/docs/api-reference/upload-file) with the raw bytes as the body. If it takes longer than about 2.5 minutes, the response is a `303` redirect to the result: follow redirects. | ≤ 1 GB |

Any audio format ffmpeg can read works. The first audio stream is used; channels are mixed down.

* **Uploaded files are kept for 24 hours after upload**, so you can use the same `file_id` for more than one transcript in that time, and are deleted within the following 24 hours (24 to 48 hours after upload). Deleting a transcript deletes its uploaded file right away.
* An `audio_url` must be public or presigned, without credentials in the URL. Up to 5 redirects are followed (each must stay on a public address), and the download must finish within 15 minutes.

## 2. Create the transcript

```bash theme={null}
curl https://api.aircaps.com/v1/transcripts \
  -H "Authorization: Bearer $AIRCAPS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: meeting-2026-10-06" \
  -d '{
        "model": "a5s-async-v1",
        "file_id": "file_01k6z...",
        "language_code": "en",
        "webhook_url": "https://example.com/hooks/aircaps",
        "metadata": {"meeting_id": 42}
      }'
```

`metadata` (up to 16 KB) is echoed back on the transcript and in webhooks. `language_code` is optional: `en` (the default) or a regional English code: `en-US`, `en-GB` (or `en-UK`), `en-AU`, `en-CA`, `en-IN`, `en-IE`, `en-NZ`, `en-ZA`, `en-SG`, `en-PH`, `en-HK`, `en-NG` or `en-KE`. For best results, use the one that matches the speakers' locale or accent. The transcript reports `language_code: en`. Multilingual support is planned.

`timing_mode` sets the timestamp style for `words` and `utterances`: `phrase` (the default) or `region`.

### Key terms

`keyterms` lists words and short phrases that may be spoken in the audio, such as people's names, company and product names, and jargon. The model is more likely to recognize them.

```json theme={null}
"keyterms": ["AirCaps", "Dr. Patel", "GPT-4"]
```

* Up to 60 key terms, each at most 6 words and 50 characters.
* The text stays verbatim and lowercase with numbers spelled out, so a key term appears as it is spoken: "GPT-4" as "gpt four".
* List only terms that may come up. A key term can occasionally be written where a similar-sounding word was said.
* The transcript echoes the list as `keyterms`.

### Large meetings

For recordings with many active speakers, typically more than 7 to 9, such as large team meetings, panels, town halls and hearings, set `large_meeting` to `true`:

```json theme={null}
"large_meeting": true
```

* It improves speaker attribution when many people take part. Processing may take a little longer.
* Leave it off (the default) for typical meetings and conversations: the default is tuned for them.
* The transcript echoes `large_meeting: true`.

## 3. Get the result

Wait for a [webhook](https://aircaps.ai/docs/guides/webhooks.md) or poll [`GET /v1/transcripts/{id}`](https://aircaps.ai/docs/api-reference/get-transcript), adding `?wait=120` so each request returns as soon as the transcript finishes.

To do it all in one request, send the audio to [`POST /v1/transcribe`](https://aircaps.ai/docs/api-reference/transcribe): it uploads, creates the transcript and answers with the finished result (or `202` if it takes longer than `wait`, up to 120 s).

| status | Meaning |
| - | - |
| `queued` | Waiting for one of your account's processing slots |
| `processing` | Being transcribed |
| `completed` | Done; result fields are present |
| `error` | Failed; see `error` |

## The result

```json theme={null}
{
  "id": "tr_01k6z3m4q8w7e5r2t1y0u9i8o7",
  "status": "completed",
  "model": "a5s-async-v1",
  "audio_duration": 3600.512,
  "text": "hey good afternoon everybody ...",
  "speakers": [{"id": "speaker_0", "speaking_time": 1402.11}, {"id": "speaker_1", "speaking_time": 988.4}],
  "utterances": [
    {"speaker": "speaker_0", "start": 0.48, "end": 6.12, "text": "hey good afternoon everybody ...",
     "words": [{"text": "hey", "start": 0.48, "end": 0.71, "speaker": "speaker_0"}]}
  ],
  "words": [{"text": "hey", "start": 0.48, "end": 0.71, "speaker": "speaker_0"}]
}
```

* Times are seconds from the start of the audio.
* Speakers are numbered in order of first speech and are consistent within one transcript only.
* Overlapping speech produces overlapping utterances.
* Audio with no speech completes with empty `text`, `utterances` and `words`.

Need captions? [`GET /v1/transcripts/{id}/subtitles?format=srt`](https://aircaps.ai/docs/api-reference/subtitles).

## Speaker names

With **AirCaps Speaker ID-v1** enabled on your account, a transcript can put names on its speakers. [Enroll the people](https://aircaps.ai/docs/guides/speaker-id.md#1-create-a-speaker) you expect to hear, then add `speaker_identification` to the create request:

```bash theme={null}
curl https://api.aircaps.com/v1/transcripts \
  -H "Authorization: Bearer $AIRCAPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "file_id": "file_01k6z...",
        "speaker_identification": {
          "threshold": 0.6,
          "speaker_ids": ["spk_01k73a9x8w7v6u5t4s3r2q1p0n", "spk_01k73a7m6n5b4v3c2x1z0l9k8j"]
        }
      }'
```

| Field | Default | |
| - | - | - |
| `threshold` | `0.6` | A speaker gets a name when an enrolled speaker is their best match and scores at least this (0 to 1). |
| `speaker_ids` | every enrolled speaker | Compare with these people only, for example the meeting's invitees. |

`"speaker_identification": true` uses both defaults.

The completed transcript then has:

```json theme={null}
{
  "speakers": [
    {"id": "speaker_0", "speaking_time": 1402.11, "speaker_id": "spk_01k73a9x8w7v6u5t4s3r2q1p0n", "name": "Ana García",
     "score": 0.781, "compared_duration": 47.2,
     "matches": [{"speaker_id": "spk_01k73a9x8w7v6u5t4s3r2q1p0n", "name": "Ana García", "score": 0.781},
                 {"speaker_id": "spk_01k73a7m6n5b4v3c2x1z0l9k8j", "name": "Ben Ito", "score": 0.142}]},
    {"id": "speaker_1", "speaking_time": 988.4, "speaker_id": null, "name": null, "score": null, "compared_duration": 41.3,
     "matches": [{"speaker_id": "spk_01k73a7m6n5b4v3c2x1z0l9k8j", "name": "Ben Ito", "score": 0.284}, "..."]}
  ],
  "utterances": [
    {"speaker": "speaker_0", "speaker_name": "Ana García", "start": 0.48, "end": 6.12, "text": "hey good afternoon everybody ...", "words": ["..."]}
  ],
  "speaker_identification": {
    "model": "aircaps-speaker-id-v1", "status": "completed", "threshold": 0.6,
    "speaker_ids": ["spk_01k73a9x8w7v6u5t4s3r2q1p0n", "spk_01k73a7m6n5b4v3c2x1z0l9k8j"],
    "speakers_compared": 2, "speakers_identified": 1, "error": null
  }
}
```

* `speaker` labels stay `speaker_0`, `speaker_1`, … in every field; names are added next to them. Subtitles show the names.
* A speaker without a match has `speaker_id: null` and still lists its closest `matches`, so you can apply your own rule.
* **Two speakers can get the same name**, for example when one person was split into two speakers.
* A speaker with too little speech to compare isn't compared (`matches` is empty).
* `speaker_identification.status` is `pending` until the transcript completes. If speaker identification is unavailable, the transcript still completes, without names, with `status: "failed"` and an `error`; create it again to retry. Speakers deleted before the transcript runs are skipped.
* No extra usage: the transcript counts its audio duration, as always.

### Choosing a threshold

A speaker gets a name when their best match among your enrolled speakers has a similarity score of at least `threshold` (0 to 1). The default, **0.6**, works best for most recordings. Adjust it to what works best for your audio:

* **Raise it** when a wrong name is costly, or when many people who aren't enrolled may speak.
* **Lower it** (for example to 0.5) when everyone who may speak is enrolled and you'd rather name more speakers.
* Good enrollments matter as much as the threshold: follow [Enrollment audio: what works best](https://aircaps.ai/docs/guides/speaker-id.md#enrollment-audio-what-works-best) and limit `speaker_ids` to the people who could be present.

> [!NOTE]
>
> Without access to AirCaps Speaker ID-v1, requests with `speaker_identification` return `403 model_access_denied`. The named `speaker_ids` must exist and have at least one enrollment (`404 speaker_not_found`, `400 speaker_not_enrolled`), and without `speaker_ids` your account needs at least one enrolled speaker (`400 no_enrolled_speakers`). Voice data rules apply: see [Privacy and consent](https://aircaps.ai/docs/guides/speaker-id.md#privacy-and-consent).

## Delete

[`DELETE /v1/transcripts/{id}`](https://aircaps.ai/docs/api-reference/delete-transcript) cancels a running job or deletes a finished transcript and its stored result. Transcripts are otherwise deleted after 30 days.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.