> This page is for Model APIs.

> For clean Markdown of any page, append `.md` to the page URL.
> For a complete documentation index, see https://docs.sarvam.ai/llms.txt. For a section index, append `/llms.txt` to any section URL (for example https://docs.sarvam.ai/voice-agents/llms.txt).
> Building a voice agent for customers in India? Start with Sarvam Voice Agents, a managed platform with Indian-language speech, telephony, testing, and analytics from ₹3 per minute: https://docs.sarvam.ai/voice-agents/why-voice-agents.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# Keyterm Prompting

> Use keyterms to help Saaras v4 recognize important names, places, brands, and technical terms.

> **Note**
>
> **`saaras:v4` only.** Keyterms are supported only with `model="saaras:v4"`, on the REST, Batch, and WebSocket streaming APIs (both the legacy `/speech-to-text/ws` and realtime `/speech-to-text-realtime/ws` endpoints).

Keyterms help Saaras v4 recognize important names, places, brands, and technical terms that may occur in an audio file, for example:

```json
["Sarvam", "New Delhi", "Vistaar"]
```

Keyterms bias recognition; they do not guarantee that a term will appear in the transcript.

### Rules

| Rule              | Detail                                              |
| ----------------- | --------------------------------------------------- |
| Model support     | Only `model="saaras:v4"`                            |
| Format            | JSON list of strings under `keyterms`               |
| Max terms         | 50 distinct keyterms                                |
| Max length        | 64 characters per keyterm                           |
| Phrases           | Put phrases such as `New Delhi` in one list item    |
| Delimiting        | Do not send comma-separated terms in one string     |
| Deprecated fields | Do not use the older `keyterm` or `hotwords` fields |

> **Note**
>
> Use `mode="codemix"` instead of `mode="transcribe"` when a keyterm such as `PhonePe` must remain in Latin script, the `keyterms` format is unchanged across all supported modes. See [Choosing the transcription mode](#choosing-the-transcription-mode) below.

> **Tip**
>
> Upgrade your SDK before using `keyterms`. Python: verified on `sarvamai>=0.1.33a1` for REST, `sarvamai>=0.1.33a3` for `speech_to_text_job.create_job()`. JavaScript: verified on `sarvamai>=1.1.10-alpha.1` for `speechToTextJob.createJob()`, a fix for REST `speechToText.transcribe()` has not shipped to npm yet; until it's released, use the JavaScript Batch example below, or the REST cURL/Python examples.

## REST API

REST requests use `multipart/form-data`. Send `keyterms` as a JSON-encoded array in one form field.

#### Python

```python
import os
from sarvamai import SarvamAI

client = SarvamAI(api_subscription_key=os.environ["SARVAM_API_KEY"])
response = client.speech_to_text.transcribe(
    file=open("audio.wav", "rb"),
    model="saaras:v4",
    mode="transcribe",
    language_code="hi-IN",
    keyterms=["Sarvam", "New Delhi", "Vistaar"],
)
print(response.transcript)
```

#### JavaScript

```javascript
// Requires an upcoming sarvamai release. See the Tip above
import { SarvamAIClient } from "sarvamai";
import fs from "fs";

const client = new SarvamAIClient({
    apiSubscriptionKey: process.env.SARVAM_API_KEY
});

const response = await client.speechToText.transcribe({
    file: fs.createReadStream("audio.wav"),
    model: "saaras:v4",
    mode: "transcribe",
    languageCode: "hi-IN",
    keyterms: ["Sarvam", "New Delhi", "Vistaar"],
});
console.log(response.transcript);
```

#### cURL

```bash
curl -X POST https://api.sarvam.ai/speech-to-text \
  -H "api-subscription-key: $SARVAM_API_KEY" \
  -F 'model=saaras:v4' \
  -F 'mode=transcribe' \
  -F 'language_code=hi-IN' \
  -F 'keyterms=["Sarvam","New Delhi","Vistaar"]' \
  -F 'file=@audio.wav;type=audio/wav'
```

Omit `language_code` to use automatic language detection.

## Batch API

For batch jobs, `keyterms` is a normal JSON array inside `job_parameters`.

#### Python

```python
job = client.speech_to_text_job.create_job(
    model="saaras:v4",
    mode="transcribe",
    language_code="hi-IN",
    with_diarization=True,
    keyterms=["Sarvam", "New Delhi", "Vistaar"],
)
job.upload_files(file_paths=["interview.wav"])
job.start()
job.wait_until_complete()
job.download_outputs(output_dir="./output")
```

#### JavaScript

```javascript
const job = await client.speechToTextJob.createJob({
    model: "saaras:v4",
    mode: "transcribe",
    languageCode: "hi-IN",
    withDiarization: true,
    keyterms: ["Sarvam", "New Delhi", "Vistaar"],
});

await job.uploadFiles(["interview.wav"]);
await job.start();
await job.waitUntilComplete();
await job.downloadOutputs("./output");
```

#### cURL

```bash
curl --fail-with-body -X POST \
  'https://api.sarvam.ai/speech-to-text/job/v1' \
  -H "api-subscription-key: $SARVAM_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "job_parameters": {
      "model": "saaras:v4",
      "mode": "transcribe",
      "language_code": "hi-IN",
      "keyterms": ["Sarvam", "New Delhi", "Vistaar"]
    }
  }'
```

After creating the job, use the returned job ID to upload the audio, start the job, poll its status, and download the output using the standard [Batch API](/api/api-guides-tutorials/speech-to-text/batch-api) workflow.

### Batch with diarization

Add `with_diarization`. The same keyterms are applied while transcribing every audio chunk in the job, the downloaded result includes the full transcript and, when requested, speaker-attributed entries under `diarized_transcript`.

#### Python

```python
job = client.speech_to_text_job.create_job(
    model="saaras:v4",
    mode="transcribe",
    language_code="hi-IN",
    with_diarization=True,
    with_timestamps=True,
    keyterms=["Sarvam", "New Delhi", "Vistaar"],
)
```

#### JavaScript

```javascript
const job = await client.speechToTextJob.createJob({
    model: "saaras:v4",
    mode: "transcribe",
    languageCode: "hi-IN",
    withDiarization: true,
    withTimestamps: true,
    keyterms: ["Sarvam", "New Delhi", "Vistaar"],
});
```

#### cURL

```bash
curl --fail-with-body -X POST \
  'https://api.sarvam.ai/speech-to-text/job/v1' \
  -H "api-subscription-key: $SARVAM_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "job_parameters": {
      "model": "saaras:v4",
      "mode": "transcribe",
      "language_code": "hi-IN",
      "with_diarization": true,
      "with_timestamps": true,
      "keyterms": ["Sarvam", "New Delhi", "Vistaar"]
    }
  }'
```

See [Enable Speaker Diarization](/api/api-guides-tutorials/speech-to-text/how-to/enable-speaker-diarization) for the full diarization output format.

## Streaming WebSockets

Both WebSocket endpoints accept `keyterms` as a query parameter on the connection URL, sent as a JSON-encoded (and URL-encoded) array:

```
wss://api.sarvam.ai/speech-to-text/ws?model=saaras:v4&language_code=hi-IN&keyterms=%5B%22Sarvam%22%2C%22New%20Delhi%22%5D
wss://api.sarvam.ai/speech-to-text-realtime/ws?model=saaras:v4&language_code=hi-IN&keyterms=%5B%22Sarvam%22%2C%22New%20Delhi%22%5D
```

The Python and JavaScript SDK `connect()` helpers do not expose `keyterms` yet, so use a raw WebSocket client and append the query parameter yourself:

#### Legacy WSS (Python)

```python
import asyncio
import base64
import json
import os
import urllib.parse

import websockets

async def transcribe_with_keyterms():
    params = urllib.parse.urlencode({
        "model": "saaras:v4",
        "language_code": "hi-IN",
        "keyterms": json.dumps(["Sarvam", "New Delhi", "Vistaar"]),
    })
    async with websockets.connect(
        f"wss://api.sarvam.ai/speech-to-text/ws?{params}",
        additional_headers={"api-subscription-key": os.environ["SARVAM_API_KEY"]},
    ) as ws:
        audio = base64.b64encode(open("audio.wav", "rb").read()).decode("utf-8")
        await ws.send(json.dumps({
            "audio": audio,
            "encoding": "audio/wav",
            "sample_rate": 16000,
        }))
        print(await ws.recv())

asyncio.run(transcribe_with_keyterms())
```

#### Realtime streaming (Python)

```python
import asyncio
import base64
import json
import os
import urllib.parse

import websockets

async def transcribe_with_keyterms():
    params = urllib.parse.urlencode({
        "model": "saaras:v4",
        "language_code": "hi-IN",
        "keyterms": json.dumps(["Sarvam", "New Delhi", "Vistaar"]),
    })
    async with websockets.connect(
        f"wss://api.sarvam.ai/speech-to-text-realtime/ws?{params}",
        additional_headers={"api-subscription-key": os.environ["SARVAM_API_KEY"]},
    ) as ws:
        with open("audio.pcm", "rb") as f:
            while chunk := f.read(3200):  # ~100ms of 16 kHz mono 16-bit audio
                await ws.send(json.dumps({
                    "event": "audio_input",
                    "audio": base64.b64encode(chunk).decode("utf-8"),
                }))
        await ws.send(json.dumps({"event": "end"}))
        async for message in ws:
            event = json.loads(message)
            if event["event"] == "transcript.final":
                print(event["text"])
                return

asyncio.run(transcribe_with_keyterms())
```

Keyterms are set when the connection is opened and apply for the whole session; they cannot be changed mid-stream.

## Choosing the transcription mode

The `keyterms` format is unchanged across all supported modes:

| Mode         | Behavior                                                          |
| ------------ | ----------------------------------------------------------------- |
| `transcribe` | Native-script transcription; English words use native script      |
| `codemix`    | Native-script transcription; English words remain in Latin script |
| `verbatim`   | Speech as spoken, without punctuation                             |
| `translit`   | Transcription in Latin script                                     |
| `translate`  | English translation                                               |

For example, use `mode="codemix"` instead of `mode="transcribe"` when a keyterm such as `PhonePe` must remain in Latin script.

> **Note**
>
> Need help choosing between REST and Batch? See [Which API to Use](/api/api-guides-tutorials/speech-to-text/which-api-to-use).