openapi: 3.0.1 info: title: Unified Speech-to-Text API description: | Unified Speech-to-Text endpoint. Path is `/waves/v1/stt/`; the model is selected via the `?model=` query parameter. Supported models: - `pulse-pro`: leaderboard-ranked English STT (5.42% ESB avg WER, #2 tied on Open ASR Leaderboard). HTTP only. - `pulse`: high-accuracy multilingual STT (21 streaming + 26 pre-recorded languages), supports pre-recorded HTTP and streaming WebSocket. The path mirrors the unified TTS shape (`POST /waves/v1/tts/` + `WS /waves/v1/tts/live`). Adding a new ASR model in the future means adding a new `?model=` value; the path stays stable. The existing endpoint `POST /waves/v1/pulse/get_text` (and its WebSocket equivalent) continues to work alongside the new unified path. New integrations are encouraged to use `/waves/v1/stt/` since it carries both models behind one path. version: 1.0.0 servers: - url: https://api.smallest.ai description: Waves API server x-fern-server-name: waves paths: /waves/v1/stt/: post: x-fern-server-name: waves tags: - Speech to Text summary: Transcribe (Pre-recorded) description: | 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. operationId: transcribe security: - BearerAuth: [] parameters: - name: model in: query required: true schema: type: string enum: [pulse-pro, pulse] example: pulse-pro description: | Selects which ASR model handles the request. Required; missing or invalid values return `400`. - `pulse-pro`: English only, leaderboard-ranked accuracy, raw bytes only; supports async via `webhook_url`. - `pulse`: multilingual (39 languages), raw bytes OR URL. - name: language in: query required: true schema: type: string enum: - 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 description: | Language of the audio file. This endpoint is **Pre-Recorded (HTTP)** — for streaming, switch to `WSS /waves/v1/stt/live` (different supported language set). **26 single-language codes:** `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`. **Regional auto-detect aggregators** for unknown audio: - `multi-eu` — auto-detects across all 21 European codes plus `en`. - `multi-asian` — auto-detects across `zh`, `ko`, `ja`, `en`. - `multi-indic`: auto-detects across `en`, `hi`, `gu`, `mr`, `bn`, `or`. India region only. - **Pulse Pro**: pass `en`. - **Pulse**: pass any of the single-language codes above, or use the `multi-eu` / `multi-asian` / `multi-indic` aggregator for unknown audio. See the [Pulse model card](/models/model-cards/speech-to-text/pulse) for the full table with language names. - name: word_timestamps in: query required: false schema: type: boolean default: false description: Include the per-word `words[]` array in the response — each entry carries the recognized `word`, its `start`/`end` timestamps, and a per-word `confidence` score (0.0–1.0). With `diarize=true`, entries also include `speaker`. On Pulse Pro this costs roughly one-third of throughput. - name: diarize in: query required: false schema: type: boolean default: false description: Multi-speaker identification; adds per-word and per-utterance speaker labels. - name: webhook_url in: query required: false schema: type: string format: uri description: | Pulse Pro only. If set, the response is `200` with `{"status": "processing", "request_id": "..."}` immediately, and the full transcription is delivered to this URL when ready. Use for long files where you do not want to hold an HTTP connection open. - name: webhook_method in: query required: false schema: type: string enum: [GET, POST] default: POST description: HTTP method to use when calling the webhook. Pulse Pro only. - name: webhook_extra in: query required: false schema: type: string description: Arbitrary metadata returned to the webhook in addition to the transcription payload. Pulse Pro only. - name: redact_pii in: query required: false schema: type: string enum: ['true', 'false'] default: 'false' description: | Redact personally identifiable information from the transcript. Names → `[FIRSTNAME_*]` / `[LASTNAME_*]`, phone numbers → `[PHONENUMBER_*]`, addresses → `[ADDRESS_*]`, etc. The redaction tokens use sequential indices so multiple occurrences of the same entity get distinct labels (`[FIRSTNAME_1]`, `[FIRSTNAME_2]`). **Language support:** currently effective only on `en` and `hi`. Setting `redact_pii=true` on other language codes is accepted but does not redact. - name: redact_pci in: query required: false schema: type: string enum: ['true', 'false'] default: 'false' description: | Redact payment card information (credit-card numbers, CVV, account numbers, etc.). Replaces matches with `[ACCOUNTNUMBER_*]` tokens. Use alongside `redact_pii=true` for full PCI-compliant transcript handling. **Language support:** currently effective only on `en` and `hi`. Setting `redact_pci=true` on other language codes is accepted but does not redact. - name: emotion_detection in: query required: false schema: type: string enum: ['true', 'false'] default: 'false' description: | When `true`, the response adds an `emotions` object mapping detected emotion labels to confidence scores. Useful for voice-of-customer analytics on call recordings. - name: gender_detection in: query required: false schema: type: string enum: ['true', 'false'] default: 'false' description: | When `true`, the response adds a `gender` field with the detected speaker gender label. Pulse pre-recorded only. requestBody: required: true content: application/octet-stream: schema: type: string format: binary description: Raw audio bytes. Set `Content-Type` to the audio MIME type (e.g. `audio/wav`). application/json: schema: type: object properties: url: type: string format: uri example: https://example.com/audio.wav required: [url] example: url: https://example.com/audio.wav responses: '200': description: | 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. content: application/json: schema: anyOf: - $ref: '#/components/schemas/TranscriptionResponse' - $ref: '#/components/schemas/AsyncAccepted' example: status: success transcription: 'Hi, how are you doing? Could you help me reschedule my appointment?' words: - { word: Hi, start: 0.32, end: 0.40, confidence: 0.96 } - { word: how, start: 0.48, end: 0.56, confidence: 0.93 } language: en metadata: duration: 5.6 processing_time_ms: 240.51 rtfx: 23.3 num_chunks: 1 request_id: 87dd36c1-4267-472d-96ee-4113e0a770a6 '400': description: 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`). content: application/json: schema: $ref: '#/components/schemas/SttErrorResponse' example: error: 'Missing required query param: model. Supported values: pulse | pulse-pro.' request_id: 87dd36c1-4267-472d-96ee-4113e0a770a6 '401': description: API key missing or invalid. content: application/json: schema: $ref: '#/components/schemas/SttErrorResponse' '403': description: Plan does not include access to the requested model. content: application/json: schema: $ref: '#/components/schemas/SttErrorResponse' '413': description: Payload exceeds 250 MB. content: application/json: schema: $ref: '#/components/schemas/SttErrorResponse' example: error: File size exceeds maximum limit of 250MB. request_id: 87dd36c1-4267-472d-96ee-4113e0a770a6 '429': description: RPM cap exceeded (Standard plan default 25/min per model). content: application/json: schema: $ref: '#/components/schemas/SttErrorResponse' example: error: Rate limit exceeded. Please retry after a short backoff. request_id: 87dd36c1-4267-472d-96ee-4113e0a770a6 '503': description: Worker temporarily unavailable. content: application/json: schema: $ref: '#/components/schemas/SttErrorResponse' components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: 'API key authentication. Include your key as `Authorization: Bearer YOUR_API_KEY`.' schemas: Word: type: object properties: word: { type: string, example: hello } start: { type: number, example: 0.32 } end: { type: number, example: 0.40 } confidence: { type: number, example: 0.96, description: "Per-word confidence score, from 0.0 to 1.0." } speaker: { type: string, description: "Present when `diarize=true`.", example: speaker_0 } Utterance: type: object properties: text: { type: string, example: Hello world. } start: { type: number, example: 0.0 } end: { type: number, example: 0.9 } speaker: { type: string, example: speaker_0 } TranscriptionResponse: type: object required: [status, transcription] properties: status: { type: string, example: success } transcription: { type: string, example: "Hi, how are you doing?" } words: type: array items: { $ref: '#/components/schemas/Word' } utterances: type: array description: "Sentence-level segments with optional speaker labels. Returned by `?model=pulse` only; Pulse Pro responses omit this field." items: { $ref: '#/components/schemas/Utterance' } language: { type: string, example: en } metadata: type: object properties: duration: { type: number, description: "Audio duration in seconds.", example: 5.6 } processing_time_ms: { type: number, description: "Pulse Pro only.", example: 240.51 } rtfx: { type: number, description: "Real-time factor for this request (Pulse Pro only).", example: 23.3 } num_chunks: { type: number, description: "Number of internal chunks the audio was split into (Pulse Pro only).", example: 1 } filename: { type: string, description: "Pulse responses include this when sent via URL.", example: audio.wav } fileSize: { type: number, description: "Bytes received (Pulse responses).", example: 268844 } request_id: { type: string, example: 87dd36c1-4267-472d-96ee-4113e0a770a6 } gender: type: string description: "Detected speaker gender label. Present when `gender_detection=true` was set on the request." example: female emotions: type: object description: "Detected emotion labels mapped to confidence scores. Present when `emotion_detection=true` was set on the request." additionalProperties: { type: number } example: { neutral: 0.72, happy: 0.18, calm: 0.10 } AsyncAccepted: type: object description: Returned by Pulse Pro when `webhook_url` is set. The transcription arrives on the webhook when ready. required: [status, request_id] properties: status: { type: string, example: processing } request_id: { type: string, example: f3a1c2e0-09c3-43e2-8d8e-cb1f3b1b8f7d } SttErrorResponse: type: object required: [error] properties: error: { type: string, description: Error message. } request_id: { type: string, description: Correlation ID for support / logs, when the server includes one. } details: type: array description: Additional error details (validation errors). items: { type: object }