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

# Batch transcription

> Transcribe recorded audio with word-level timestamps at \$0.30 per hour of audio

Hopper serves [Nemotron ASR](/models#nemotron-asr), NVIDIA's open speech-to-text model, through an OpenAI-compatible transcription endpoint. Send a complete audio file to `POST https://api.withhopper.com/v1/audio/transcriptions` and the response returns the transcript with word-level timestamps. For live audio, use the [streaming WebSocket](/stt/streaming).

## Request

The request is `multipart/form-data`; any other content type returns 400 `invalid_content_type`.

| Field         | Type   | Constraint                                                                                                                                                                                                  | Default |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `file`        | file   | Required. Raw PCM16 mono or WAV only. Compressed formats (mp3, ogg, flac, mp4, m4a) are detected by magic bytes and rejected with 400 `unsupported_audio_format`. Total body capped at 25 MB (413 past it). | —       |
| `model`       | string | Required; 400 `missing_model` without it. Use `nemotron-asr`.                                                                                                                                               | —       |
| `language`    | string | Optional language hint, forwarded to the model.                                                                                                                                                             | none    |
| `keyterms`    | string | Optional comma-separated list of terms to bias recognition toward (names, product terms, jargon).                                                                                                           | none    |
| `sample_rate` | int    | Must be in the model's allowlist — for `nemotron-asr`: 8000, 16000, 22050, 24000, 44100, 48000. Anything else returns 400 `invalid_sample_rate`.                                                            | 16000   |

Decode compressed audio to PCM16 or WAV client-side before uploading; the gateway does not transcode.

<CodeGroup>
  ```python Python theme={null}
  import requests

  resp = requests.post(
      "https://api.withhopper.com/v1/audio/transcriptions",
      headers={"Authorization": "Bearer sk_hopper_..."},
      files={"file": open("call.wav", "rb")},
      data={"model": "nemotron-asr", "sample_rate": 16000},
  )
  result = resp.json()
  print(result["text"])
  print(resp.headers["x-hopper-request-cost-usd"])
  ```

  ```typescript TypeScript theme={null}
  import { readFile } from "node:fs/promises";

  const form = new FormData();
  form.append("file", new Blob([await readFile("call.wav")]), "call.wav");
  form.append("model", "nemotron-asr");
  form.append("sample_rate", "16000");

  const res = await fetch("https://api.withhopper.com/v1/audio/transcriptions", {
    method: "POST",
    headers: { Authorization: "Bearer sk_hopper_..." },
    body: form,
  });
  const { text, words, duration_seconds } = await res.json();
  ```

  ```bash cURL theme={null}
  curl https://api.withhopper.com/v1/audio/transcriptions \
    -H "Authorization: Bearer sk_hopper_..." \
    -F file=@call.wav \
    -F model=nemotron-asr \
    -F sample_rate=16000
  ```
</CodeGroup>

## Response

```json theme={null}
{
  "text": "thanks for calling, how can I help",
  "words": [
    { "word": "thanks", "start": 0.12, "end": 0.34 }
  ],
  "duration_seconds": 12.345
}
```

`words` carries start and end offsets in seconds for every word in the finalized transcript. `duration_seconds` is derived from the upload, rounded to the millisecond:

```
duration_seconds = bytes / (sample_rate × 2)   # PCM16 mono: 2 bytes per sample
```

Every response includes exact spend headers: `x-hopper-request-cost-usd` for this request and `x-hopper-credits-remaining-usd` for the balance after it.

## Billing

Transcription costs \$0.30 per hour of audio (\$0.005 per minute), metered on `duration_seconds`. A 10-minute call costs \$0.05.

## Errors

| Status | Code                       | When                                        |
| ------ | -------------------------- | ------------------------------------------- |
| 400    | `invalid_content_type`     | Body is not `multipart/form-data`           |
| 400    | `missing_model`            | No `model` field                            |
| 400    | `unsupported_audio_format` | Compressed audio (mp3, ogg, flac, mp4, m4a) |
| 400    | `invalid_sample_rate`      | `sample_rate` not in the model's allowlist  |
| 413    | `body_too_large`           | Body over 25 MB                             |
| 502    | `upstream_unavailable`     | Transcription backend unreachable           |
| 503    | `model_offline`            | Model has no healthy upstream               |

Authentication, rate-limit (429), and credit (402) errors are shared across the API — see [Errors](/platform/errors).
