---
title: "Transcribe (Pre-recorded)"
method: POST
path: "/waves/v1/stt/"
tags: ["Speech to Text"]
---

# Transcribe (Pre-recorded)

`POST /waves/v1/stt/`

Transcribe an audio file. The model is chosen via `?model=`:

- `?model=pulse-pro`: English-only, leaderboard-ranked accuracy. Raw bytes only; pass `webhook_url` to receive transcription asynchronously on long files.
- `?model=pulse`: multilingual transcription (21 streaming + 26 pre-recorded languages), supports both raw bytes and audio-by-URL.

## When to use this

Use this endpoint when you have a complete audio file (call recording, voicemail, podcast episode) and want the transcript back in one response. For live transcription as audio arrives, use the realtime WebSocket endpoint (`WS /waves/v1/stt/live`) instead.

Pulse Pro has no streaming worker today; calls to `WS /waves/v1/stt/live?model=pulse-pro` return `400` before the WebSocket upgrades.

## Input methods

- **Raw bytes**: `Content-Type: application/octet-stream` with the audio in the body. All knobs are query parameters.
- **URL (`?model=pulse` only)**: `Content-Type: application/json` with `{"url": "..."}` in the body.

## Examples

**cURL**: Pulse Pro, sync
```bash
curl -X POST "https://api.smallest.ai/waves/v1/stt/?model=pulse-pro&language=en&word_timestamps=true" \
  -H "Authorization: Bearer $SMALLEST_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary "@./call.wav"
```

**cURL**: Pulse Pro, async via webhook
```bash
curl -X POST "https://api.smallest.ai/waves/v1/stt/?model=pulse-pro&language=en&webhook_url=https://your.app/cb" \
  -H "Authorization: Bearer $SMALLEST_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary "@./call.wav"
```
Returns `200 { "status": "processing", "request_id": "..." }` immediately. The webhook receives the full transcription when ready.

**cURL**: Pulse, audio-by-URL
```bash
curl -X POST "https://api.smallest.ai/waves/v1/stt/?model=pulse&language=en" \
  -H "Authorization: Bearer $SMALLEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-bucket.s3.amazonaws.com/call.wav"}'
```

**Python**
```python
import requests

with open("./call.wav", "rb") as f:
    audio = f.read()

r = requests.post(
    "https://api.smallest.ai/waves/v1/stt/",
    params={"model": "pulse-pro", "language": "en", "word_timestamps": "true"},
    headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/octet-stream"},
    data=audio,
)
r.raise_for_status()
print(r.json()["transcription"])
```

**JavaScript / TypeScript**
```typescript
import { readFileSync } from "node:fs";

const audio = readFileSync("./call.wav");
const params = new URLSearchParams({ model: "pulse-pro", language: "en", word_timestamps: "true" });

const res = await fetch(`https://api.smallest.ai/waves/v1/stt/?${params}`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`, "Content-Type": "application/octet-stream" },
  body: audio,
});
console.log((await res.json()).transcription);
```

## Common gotchas

- **`model` is required.** Missing or invalid values return `400` with an enum-validation error.
- **Pulse Pro is English only.** Pass `language=en`. Other language codes are accepted at the wire level but produce unpredictable output.
- **Pulse Pro does not support audio-by-URL.** Send raw bytes or use `?model=pulse` for the URL flow.
- **Async (webhook) mode is Pulse Pro only.** Pulse runs sync only on this endpoint.
- **Max payload 250 MB.** Larger requests return `413`. Compress to mono 16 kHz PCM if you are close to the limit; quality is unaffected.

## Query parameters

- `model` 'pulse-pro' | 'pulse', required
- `language` 'en' | 'hi' | 'de' | 'es' | 'ru' | 'it' | 'fr' | 'nl' | 'pt' | 'uk' | 'pl' | 'cs' | 'sk' | 'lv' | 'et' | 'ro' | 'fi' | 'sv' | 'bg' | 'hu' | 'da' | 'lt' | 'mt' | 'zh' | 'ja' | 'ko' | 'multi-eu' | 'multi-asian' | 'multi-indic', required
- `word_timestamps` boolean
- `diarize` boolean
- `webhook_url` string, uri
- `webhook_method` 'GET' | 'POST'
- `webhook_extra` string
- `redact_pii` 'true' | 'false'
- `redact_pci` 'true' | 'false'
- `emotion_detection` 'true' | 'false'
- `gender_detection` 'true' | 'false'

## Headers

- `x-expire-content` 'true'

## Request body

- object
  - `url` string, uri, required

## Response `200`

Transcription succeeded. The response body has two shapes:

- **Sync**: full `TranscriptionResponse` with `transcription`, `words`, `metadata`, etc. Returned when `webhook_url` is not set (all `?model=pulse` requests, and `?model=pulse-pro` requests without a webhook).
- **Async**: `{ "status": "processing", "request_id": "..." }`. Returned when `?model=pulse-pro` is paired with `webhook_url`. The full `TranscriptionResponse` then arrives on the webhook when ready.

- union
  - TranscriptionResponse
    - `status` string, required
    - `transcription` string, required
    - `words` Word[] — Per-word timestamps. **Empty unless the request sets `word_timestamps=true`.** Each entry carries `word`, `start`, `end`, and `confidence` (0.0–1.0). Pulse responses with `diarize=true` also include `speaker` and `speaker_confidence`.
      - `word` string
      - `start` number
      - `end` number
      - `confidence` number — Per-word confidence score, from 0.0 to 1.0.
      - `speaker` integer — Zero-indexed speaker label. Present on Pulse when `diarize=true`. Pulse Pro does not diarize, so this field is absent on Pro responses.
      - `speaker_confidence` number — Speaker-attribution confidence for this word, from 0.0 to 1.0. Present on Pulse alongside `speaker`.
    - `utterances` Utterance[] — Sentence-level segments. Returned by `?model=pulse` only; Pulse Pro responses omit this field entirely. **Empty on Pulse unless the request sets `word_timestamps=true`** (the same flag turns on both `words[]` and `utterances[]`).
      - `text` string
      - `start` number
      - `end` number
      - `speaker` integer — Zero-indexed speaker label. Present when `diarize=true` was set on the request.
    - `language` string — Language of the transcription. Present on Pulse Pro responses; Pulse responses omit this field.
    - `metadata` object — Response metadata. Pulse responses carry `duration` and `fileSize`. Pulse Pro responses carry `duration`, `processing_time_ms`, `rtfx`, and `num_chunks`.
      - `duration` number — Audio duration in seconds. Present on both Pulse and Pulse Pro.
      - `processing_time_ms` number — Server-side processing time in milliseconds. Pulse Pro only.
      - `rtfx` number — Real-time factor for this request. Pulse Pro only.
      - `num_chunks` number — Number of internal chunks the audio was split into. Pulse Pro only.
      - `fileSize` number — Bytes received. Pulse only.
    - `request_id` string — Server-assigned request identifier. Present on Pulse Pro responses; Pulse responses omit this field.
    - `totalBytes` number — Bytes received. Pulse Pro only.
    - `gender` string — Detected speaker gender label. Present when `gender_detection=true` was set on the request.
    - `emotions` object — Detected emotion labels mapped to confidence scores. Present when `emotion_detection=true` was set on the request.
  - AsyncAccepted — Returned by Pulse Pro when `webhook_url` is set. The transcription arrives on the webhook when ready.
    - `status` string, required
    - `request_id` string, required

## Other responses

- `400` — Missing or invalid `model` query parameter, invalid params, or unsupported feature combination (e.g. `?model=pulse-pro` on the WS endpoint, audio-by-URL with `?model=pulse-pro`).
- `401` — API key missing or invalid.
- `403` — Plan does not include access to the requested model.
- `413` — Payload exceeds 250 MB.
- `429` — RPM cap exceeded (Standard plan default 25/min per model).
- `503` — Worker temporarily unavailable.

---

[API](https://skmtc.net/smallest-inc/apis/unified-speech-to-text-api.md) · [All operations](https://skmtc.net/smallest-inc/apis/unified-speech-to-text-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/smallest-inc/unified-speech-to-text-api/revisions/8f341a58147a/schema)
