> This page is for API Reference.

> 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.

# REST

POST https://api.sarvam.ai/text-to-speech
Content-Type: application/json

Convert text into spoken audio. The output is a base64-encoded audio string that must be decoded before use.

**Available Models:**
- **bulbul:v4-flash**: Latest model, recommended for new integrations — same request/response contract as bulbul:v3, with a 200+ voice persona catalog
- **bulbul:v3**: Stable model (API default when `model` is omitted) with 30+ voices and temperature control
- **bulbul:v2**: Legacy model with pitch and loudness controls

**Important Notes for bulbul:v3 and bulbul:v4-flash:**
- Pitch and loudness parameters are NOT supported
- Pace range: 0.5 to 2.0
- Preprocessing is automatically enabled
- Default sample rate is 22050 Hz when `speech_sample_rate` is omitted
- Supports sample rates: 8000, 16000, 22050, 24000 Hz (REST API also supports 32000, 44100, 48000 Hz)

Reference: https://docs.sarvam.ai/api-reference/text-to-speech/convert

## Authentication

- `api-subscription-key` header (required) — API Key authentication via header

## Request

### Body (application/json)

This endpoint expects a Sarvam_Model_API_TextToSpeechRequest.

- `text` (SarvamModelApiTextToSpeechRequestText, required) — The text(s) to be converted into speech. **Features:** - Supports code-mixed text (English and Indic languages) **Model-specific limits:** - **bulbul:v3:** Max 2500 characters - **bulbul:v4-flash:** Max 2500 characters - **bulbul:v2:** Max 1500 characters **Important Note:** - For numbers larger than 4 digits, use commas (e.g., '10,000' instead of '10000') - This ensures proper pronunciation as a whole number
- `language_code` (enum, required) — The language code in BCP-47 format.
  - Allowed values: `bn-IN`, `en-IN`, `gu-IN`, `hi-IN`, `kn-IN`, `ml-IN`, `mr-IN`, `od-IN`, `pa-IN`, `ta-IN`, `te-IN`
- `speaker` (enum, optional, nullable, default: shubh) — The speaker voice to be used for the output audio. **Default:** shubh (for bulbul:v3), anushka (for bulbul:v2), shubh\_enhi\_ads (for bulbul:v4-flash). For v4, you can omit `speaker` to use `shubh_enhi_ads`, or set any persona ID from the Voices guide (v3/v2 short names are not valid). **Model Compatibility (Speakers compatible with respective model):** * **bulbul:v3:** * shubh (default), aditya, ritu, priya, neha, rahul, pooja, rohan, simran, kavya, amit, dev, ishita, shreya, ratan, varun, manan, sumit, roopa, kabir, aayan, ashutosh, advait, anand, tanya, tarun, sunny, mani, gokul, vijay, shruti, suhani, mohit, kavitha, rehan, soham, rupali * **bulbul:v4-flash:** * 200+ persona voices named `<name>_<language>_<style>` (e.g. `shubh_hi_devotional`, `simran_en_customer`). See the [Voices guide](https://docs.sarvam.ai/api/api-guides-tutorials/text-to-speech/voices#bulbul-v4-flash-voices) for the full catalog. * **bulbul:v2:** * Female: anushka, manisha, vidya, arya * Male: abhilash, karun, hitesh **Note:** Speaker selection must match the chosen model version. **Important:** Speaker names are case-sensitive and must be lowercase (e.g., `ritu` not `Ritu`).
  - Allowed values: `anushka`, `abhilash`, `manisha`, `vidya`, `arya`, `karun`, `hitesh`, `aditya`, `ritu`, `priya`, `neha`, `rahul`, `pooja`, `rohan`, `simran`, `kavya`, `amit`, `dev`, `ishita`, `shreya`, `ratan`, `varun`, `manan`, `sumit`, `roopa`, `kabir`, `aayan`, `shubh`, `ashutosh`, `advait`, `anand`, `tanya`, `tarun`, `sunny`, `mani`, `gokul`, `vijay`, `shruti`, `suhani`, `mohit`, `kavitha`, `rehan`, `soham`, `rupali`, `bappa_bn_conversation`, `roopa_bn_conversational`, `bimal_bn_suspense`, `aditi_en_stories`, `aparna_en_companion`, `aparna_en_edtech`, `ashwin_en_sports`, `ashwin_en_sports_energetic`, `chandrika_en_stories`, `dev_en_recovery`, `dev_en_conversational`, `deven_en_conversation`, `ishita_en_customer`, `ishita_en_medical`, `ishita_en_numbers`, `ishita_en_social`, `ishita_en_stories`, `kalpit_en_edtech`, `nachiket_en_ads`, `neha_en_customer`, `neha_en_latenight`, `nupur_en_kids`, `ojas_en_social`, `ritu_en_edtech`, `ritu_en_latenight`, `ritu_en_medical`, `ritu_en_reels`, `rohan_en_recovery`, `roopa_en_conversational`, `rustom_en_suspense`, `sanchita_en_companion`, `sanchita_en_insurance`, `sanchita_en_recovery`, `sanchita_en_market`, `sanchita_en_social`, `shabana_en_edtech`, `shalini_en_companion`, `shalini_en_customer`, `shubh_en_narration`, `shubh_en_numbers`, `shubh_en_ads`, `shubh_en_recovery`, `shubh_en_audiobook`, `shubh_en_narration_gentle`, `shubh_en_sports`, `simran_en_narration`, `simran_en_automobile`, `simran_en_conversation`, `simran_en_customer`, `simran_en_edtech`, `simran_en_edtech_bot`, `simran_en_sales`, `simran_en_recovery`, `simran_en_ads`, `simran_en_therapist`, `sunny_en_social`, `varun_en_ads`, `varun_en_suspense`, `zarina_en_conversation`, `amelia_en_conversational`, `sophia_en_conversational`, `girish_en_documentary`, `girish_en_devotional`, `payal_en_edtech`, `sarang_en_narration`, `ishita_enhi_companion`, `ishita_enhi_customer`, `ishita_enhi_customer_expressive`, `sanchita_enhi_companion`, `shalini_enhi_companion`, `shalini_enhi_customer`, `shubh_enhi_companion`, `shubh_enhi_ads`, `shubh_enhi_banking`, `simran_enhi_companion`, `simran_enhi_customer`, `simran_enhi_banking_expressive`, `sunny_enhi_customer`, `bhavik_gu_conversation`, `pooja_gu_conversational`, `pooja_gu_customer`, `aayan_hi_conversational`, `amit_hi_conversational`, `ashutosh_hi_conversational`, `kabir_hi_conversational`, `kavya_hi_conversational`, `manan_hi_conversational`, `rahul_hi_conversational`, `sumit_hi_conversational`, `aditya_hi_conversational`, `aditya_hi_sales`, `anand_hi_documentary`, `anand_hi_news`, `aparna_hi_customer`, `aparna_hi_kyc`, `ashok_hi_character`, `ashok_hi_news`, `chhavi_hi_kids`, `ishita_hi_ads`, `ishita_hi_edtech`, `ishita_hi_banking`, `ishita_hi_ads_informal`, `ishita_hi_devotional`, `ishita_hi_numbers`, `ishita_hi_social`, `kunal_hi_kids`, `mahesh_hi_documentary`, `mani_hi_devotional`, `mani_hi_conversational`, `mohit_hi_conversational`, `nachiket_hi_devotional`, `priya_hi_recovery`, `ratan_hi_latenight`, `ratan_hi_customer_expressive`, `ratan_hi_documentary`, `ratan_hi_devotional`, `ratan_hi_recovery`, `ratan_hi_social`, `ratan_hi_sports`, `ratan_hi_latenight_warm`, `rehan_hi_social`, `ritu_hi_customer_utility`, `ritu_hi_kids`, `ritu_hi_conversation`, `ritu_hi_customer`, `ritu_hi_edtech`, `ritu_hi_ads_formal`, `ritu_hi_banking`, `ritu_hi_ads_informal`, `ritu_hi_insurance`, `ritu_hi_edtech_bot`, `ritu_hi_medical`, `ritu_hi_sales`, `ritu_hi_reels`, `ritu_hi_social`, `ritu_hi_customer_warm`, `ritu_hi_social_lively`, `roopa_hi_companion`, `roopa_hi_narration`, `roopa_hi_recovery`, `roopa_hi_market`, `roopa_hi_conversational`, `sanchita_hi_assistant`, `sanchita_hi_edtech`, `sanchita_hi_banking`, `sanchita_hi_feedback`, `sanchita_hi_ads_formal`, `sanchita_hi_ads_informal`, `sanchita_hi_interview`, `sanchita_hi_romantic`, `sanchita_hi_market`, `sanchita_hi_social`, `sanchita_hi_kyc`, `sarika_hi_conversation`, `shalini_hi_companion`, `shalini_hi_social`, `shreya_hi_conversational`, `shreya_hi_news`, `shruti_hi_edtech`, `shubh_hi_customer`, `shubh_hi_ecomm`, `shubh_hi_stories_mixed`, `shubh_hi_devotional`, `shubh_hi_ads`, `shubh_hi_recovery`, `shubh_hi_stories_dramatic`, `simran_hi_assistant`, `simran_hi_narration`, `simran_hi_automobile`, `simran_hi_conversation`, `simran_hi_news_breaking`, `simran_hi_social_energetic`, `simran_hi_social_excited`, `simran_hi_latenight`, `simran_hi_news`, `simran_hi_recovery`, `simran_hi_sales`, `suchitra_hi_ecomm`, `suhani_hi_social`, `sunny_hi_ads`, `sunny_hi_reels`, `tarun_hi_conversational`, `tarun_hi_sales`, `chaitra_hi_customer`, `shilpa_hi_narration`, `tanya_hi_narration`, `aarti_hi_customer`, `advait_hi_character`, `aryaman_hi_ads`, `chirag_hi_social`, `girish_hi_devotional`, `mukul_hi_ads`, `mukul_hi_suspense`, `suman_hi_companion`, `vaibhav_hi_social`, `vandana_hi_ecomm`, `vipul_hi_social`, `chaitra_kn_conversation`, `chaitra_kn_narration`, `chetan_kn_conversation`, `suchitra_kn_narration`, `ishita_mr_conversational`, `mrunal_mr_narration`, `neha_mr_narration`, `nilesh_mr_conversation`, `ritu_mr_insurance`, `ritu_mr_narration`, `rupali_mr_stories`, `soham_mr_narration`, `mukul_mr_stories`, `anand_pa_conversation`, `anand_pa_customer`, `harpreet_pa_narration`, `jaspal_pa_banking`, `gokul_ta_narration`, `vetri_ta_ads`, `vetri_ta_suspense`, `vijay_ta_narration`, `kavitha_te_conversation`, `kavitha_te_narration`, `pooja_te_conversation`, `tarun_te_narration`
- `pitch` (double, optional, nullable) — Controls the pitch of the audio. Lower values result in a deeper voice, while higher values make it sharper. The suitable range is between -0.75 and 0.75. Default is 0.0. **Note:** This parameter is only supported for bulbul:v2. It is NOT supported for bulbul:v3 or bulbul:v4-flash.
- `pace` (double, optional, nullable, default: 1) — Controls the speed of the audio. Lower values result in slower speech, while higher values make it faster. Default is 1.0. **Model-specific ranges:** - **bulbul:v3:** 0.5 to 2.0 - **bulbul:v4-flash:** 0.5 to 2.0 - **bulbul:v2:** 0.3 to 3.0
- `loudness` (double, optional, nullable) — Controls the loudness of the audio. Lower values result in quieter audio, while higher values make it louder. The suitable range is between 0.3 and 3.0. Default is 1.0. **Note:** This parameter is only supported for bulbul:v2. It is NOT supported for bulbul:v3 or bulbul:v4-flash.
- `speech_sample_rate` (enum, optional, nullable, default: 22050) — Specifies the sample rate of the output audio. Supported values are 8000, 16000, 22050, 24000, 32000, 44100, 48000 Hz. **Note:** Higher sample rates (32000, 44100, 48000 Hz) are only available with bulbul:v3 and bulbul:v4-flash via the REST API, not in streaming mode. **Default:** 22050 Hz
  - Allowed values: `8000`, `16000`, `22050`, `24000`, `32000`, `44100`, `48000`
- `enable_preprocessing` (boolean, optional, default: false) — Controls whether normalization of English words and numeric entities (e.g., numbers, dates) is performed. Set to true for better handling of mixed-language text. **Model-specific behavior:** - **bulbul:v3:** Not Supported - **bulbul:v4-flash:** Not Supported - **bulbul:v2:** Default is false
- `model` (enum, optional) — Specifies the model to use for text-to-speech conversion. **Available models:** - **bulbul:v4-flash:** Latest model, recommended for new integrations — same contract as bulbul:v3, with a 200+ voice persona catalog - **bulbul:v3:** Stable model (API default when `model` is omitted) with 30+ voices, pace, and temperature control - **bulbul:v2:** Legacy model with pitch, loudness, and pace controls
  - Allowed values: `bulbul:v2`, `bulbul:v3`, `bulbul:v4-flash`
- `output_audio_codec` (enum, optional, nullable, default: wav) — Specifies the audio codec for the output audio file. Different codecs offer various compression and quality characteristics. **Note:** On `/text-to-speech` with **bulbul:v3** and **bulbul:v4-flash**, `opus` is listed for schema compatibility but currently returns a validation error; use `wav`, `mp3`, `aac`, `flac`, or `linear16`.
  - Allowed values: `mp3`, `linear16`, `mulaw`, `alaw`, `opus`, `flac`, `aac`, `wav`
- `temperature` (double, optional, nullable, default: 0.6) — Temperature controls how much randomness and expressiveness the TTS model uses while generating speech. Lower values produce more stable and consistent output, while higher values sound more expressive but may introduce artifacts or errors. The suitable range is between 0.01 and 1.0. Default is 0.6. **Note:** This parameter is only supported for bulbul:v3 and bulbul:v4-flash. It has no effect on bulbul:v2.
- `dict_id` (string, optional, nullable) — The ID of a pronunciation dictionary to apply during synthesis. When provided, matching words in the input text will be replaced with their custom pronunciations before generating speech. Create and manage dictionaries via the [Pronunciation Dictionary API](https://docs.sarvam.ai/api-reference-docs/pronunciation-dictionary/create). Only supported by **bulbul:v3** and **bulbul:v4-flash**.
- `enable_cached_responses` (boolean, optional, default: false) — Enable caching for the request. When enabled, identical requests will return cached audio instead of regenerating. Default is false. **Note:** Currently in beta and only available for bulbul:v1 and bulbul:v2 models.

## Response

### 200

Successful Response

- `request_id` (string, required, nullable)
- `audios` (list of string, required) — The output audio files in WAV format, encoded as base64 strings. Each string corresponds to one of the input texts.

## Errors

### 400 Bad Request Error

Bad Request

- `error` (Sarvam_Model_API_ErrorDetails, required) — Error details

### 403 Forbidden Error

Forbidden

- `error` (Sarvam_Model_API_ErrorDetails, required) — Error details

### 422 Unprocessable Entity Error

Unprocessable Entity

- `error` (Sarvam_Model_API_ErrorDetails, required) — Error details

### 429 Too Many Requests Error

Quota Exceeded

- `error` (Sarvam_Model_API_ErrorDetails, required) — Error details

### 500 Internal Server Error

Internal Server Error

- `error` (Sarvam_Model_API_ErrorDetails, required) — Error details

## Types

### SarvamModelApiTextToSpeechRequestText

The text(s) to be converted into speech. **Features:** - Supports code-mixed text (English and Indic languages) **Model-specific limits:** - **bulbul:v3:** Max 2500 characters - **bulbul:v4-flash:** Max 2500 characters - **bulbul:v2:** Max 1500 characters **Important Note:** - For numbers larger than 4 digits, use commas (e.g., '10,000' instead of '10000') - This ensures proper pronunciation as a whole number

### Sarvam_Model_API_ErrorDetails

- `request_id` (string, required, nullable)
- `message` (string, required) — Message describing the error
- `code` (enum, required) — Error code for the specific error that has occurred. Refer to the error code documentation for more details.
  - Allowed values: `invalid_request_error`, `internal_server_error`, `unprocessable_entity_error`, `insufficient_quota_error`, `invalid_api_key_error`, `authentication_error`, `not_found_error`, `rate_limit_exceeded_error`, `model_call_error`, `gateway_timeout_error`, `billing_service_unavailable_error`

## Examples

**Request**

```json
{
  "text": "Welcome to Sarvam AI!",
  "language_code": "hi-IN"
}
```

**Response**

```json
{
  "request_id": "20250101_0a1b2c3d-1234-5678-9abc-def012345678",
  "audios": [
    "UklGRiQAAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YQAAAAA="
  ]
}
```

**SDK Code**

```typescript
import { SarvamAIClient } from "sarvamai";
import fs from "fs";

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

const response = await client.textToSpeech.convert({
  text: "Welcome to Sarvam AI!",
  language_code: "hi-IN",
});

const audio = Buffer.from(response.audios.join(""), "base64");
fs.writeFileSync("output.wav", audio);

```

```python
import requests

url = "https://api.sarvam.ai/text-to-speech"

payload = {
    "text": "Welcome to Sarvam AI!",
    "language_code": "hi-IN"
}
headers = {
    "api-subscription-key": "<apiSubscriptionKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.sarvam.ai/text-to-speech"

	payload := strings.NewReader("{\n  \"text\": \"Welcome to Sarvam AI!\",\n  \"language_code\": \"hi-IN\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("api-subscription-key", "<apiSubscriptionKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.sarvam.ai/text-to-speech")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["api-subscription-key"] = '<apiSubscriptionKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"text\": \"Welcome to Sarvam AI!\",\n  \"language_code\": \"hi-IN\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.sarvam.ai/text-to-speech")
  .header("api-subscription-key", "<apiSubscriptionKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"text\": \"Welcome to Sarvam AI!\",\n  \"language_code\": \"hi-IN\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.sarvam.ai/text-to-speech', [
  'body' => '{
  "text": "Welcome to Sarvam AI!",
  "language_code": "hi-IN"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'api-subscription-key' => '<apiSubscriptionKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.sarvam.ai/text-to-speech");
var request = new RestRequest(Method.POST);
request.AddHeader("api-subscription-key", "<apiSubscriptionKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"text\": \"Welcome to Sarvam AI!\",\n  \"language_code\": \"hi-IN\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "api-subscription-key": "<apiSubscriptionKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "text": "Welcome to Sarvam AI!",
  "language_code": "hi-IN"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.sarvam.ai/text-to-speech")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```