> 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 full documentation content in one file, see https://docs.sarvam.ai/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# REST Stream

POST https://api.sarvam.ai/voices/clone/stream
Content-Type: multipart/form-data

Converts the input text into a streamed spoken audio response using a cloned voice.

**Step 1:** Create a voice with `POST /voices/create` and save the returned `voice_id`.

**Step 2:** Pass that `voice_id` with `text` and `language_code` to this endpoint. The response is a binary audio stream (not JSON), so you can begin playback as soon as the first sentence is ready.

Accepts `multipart/form-data`.

**Base URL:** `https://api.sarvam.ai`.
**Auth:** send your key in the `api-subscription-key` header (recommended); `Authorization: Bearer <key>` is also accepted.

**Streaming codecs:** `mp3` (default), `wav`, `linear16`, `mulaw`, `alaw`. `flac`, `aac`, and `opus` are not supported on this endpoint because each sentence would be a complete container that cannot be concatenated.

Quality checks (QC/VAD) and duration bounds (`min_audio_duration` / `max_audio_duration`) are not accepted — the stream path optimizes for time-to-first-byte.

**Billing:** charged per character of `text` against the `text_to_speech_voice_cloning` API. See the [pricing page](https://docs.sarvam.ai/api/getting-started/pricing).

Reference: https://docs.sarvam.ai/api-reference/voice-cloning/clone-stream

## Authentication

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

## Request

### Body (multipart/form-data)

This endpoint expects a multipart form.

- `voice_id` (string, required) — Required cloned voice ID (`svc-{uuid}`). Create one with `POST /voices/create`, or use a voice from the same organization's Content Studio Voice Library.
- `text` (string, required) — Text to synthesize in the cloned voice. Native scripts and code-mixed text are supported. Maximum 1000 characters; longer text is rejected with a 400. Split longer input client-side and synthesize it in parts.
- `language_code` (enum, required) — BCP-47 language code for the synthesized output. The cloned voice can speak any supported language, regardless of the reference clip's language (cross-lingual cloning). **Available Options:** - `as-IN`: Assamese - `bn-IN`: Bengali - `en-IN`: English (Indian) - `gu-IN`: Gujarati - `hi-IN`: Hindi - `kn-IN`: Kannada - `ml-IN`: Malayalam - `mr-IN`: Marathi - `od-IN`: Odia - `pa-IN`: Punjabi - `ta-IN`: Tamil - `te-IN`: Telugu
- `pace` (double, optional) — Speech speed multiplier. `1.0` is natural speed; `0.5` to `2.0` is accepted. Speed is adjusted without changing pitch.
- `output_audio_codec` (enum, optional) — Audio codec for the streamed output. Default is `mp3`. - `mp3`: Self-framing; clients can decode and play as chunks arrive. Recommended. - `wav`: One RIFF header (unknown length), then raw PCM chunks. - `linear16`: Raw 16-bit PCM (no header). - `mulaw` / `alaw`: 8-bit telephony codecs for IVR pipelines.
- `output_audio_bitrate` (enum, optional) — Bitrate for lossy codecs (`mp3`). Default is `128k`. Options: `32k`, `64k`, `96k`, `128k`, `192k`.
- `speech_sample_rate` (enum, optional) — Output sample rate in Hz. Supported values are 8000, 16000, 22050, 24000, 32000, 44100, 48000. If omitted, the API returns 24000 Hz.
- `enable_text_normalization` (boolean, optional) — Enable pre-TTS text normalization: numbers, currencies and dates are converted to their spoken form before synthesis (e.g. `9840950950` becomes the spoken digit sequence). Defaults to the server-side setting.

## Response

### 200

Success. Returns a streamed audio response in the requested format (e.g., `audio/mpeg` for MP3, `audio/wav` for WAV).

- File download.

## Errors

### 400 Bad Request Error

Bad Request. Missing or invalid `voice_id`, empty `text`, or unsupported stream codec.

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

### 402 Payment Required Error

Quota Exceeded

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

### 403 Forbidden Error

Forbidden. The `api-subscription-key` is missing, malformed, invalid.

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

### 422 Unprocessable Entity Error

Unprocessable Entity. The form fields failed validation; the body is invalid. Check enum values and required fields.

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

### 429 Too Many Requests Error

Rate Limit Exceeded. Retry with exponential backoff.

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

### 500 Internal Server Error

Internal Server Error. Model unavailable or generation failed before the first audio byte. Retry once; if it persists, log the `request_id` and contact support.

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

### 502 Bad Gateway Error

Billing Service Unavailable. The billing service could not record usage for this request. Retry with exponential backoff; if it persists, contact support with the request_id.

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

### 503 Service Unavailable Error

Model Unavailable or Overloaded. The model call failed or the model is overloaded. Retry with exponential backoff.

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

### 504 Gateway Timeout Error

Gateway Timeout. The model call timed out. Retry with exponential backoff.

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

## Types

### 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
{
  "language_code": "hi-IN",
  "output_audio_codec": "mp3",
  "text": "नमस्ते। यह एक स्ट्रीमिंग टेस्ट है।",
  "voice_id": "svc-123e4567-e89b-12d3-a456-426614174000"
}
```

**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.voiceCloning.textToSpeechStream({
  voiceId: "svc-123e4567-e89b-12d3-a456-426614174000",
  text: "\u0928\u092e\u0938\u094d\u0924\u0947\u0964 \u092f\u0939 \u090f\u0915 \u0938\u094d\u091f\u094d\u0930\u0940\u092e\u093f\u0902\u0917 \u091f\u0947\u0938\u094d\u091f \u0939\u0948\u0964",
  language_code: "hi-IN",
  output_audio_codec: "mp3",
});

const audio = Buffer.from(await response.arrayBuffer());
fs.writeFileSync("output.mp3", audio);

```

```python
import requests

url = "https://api.sarvam.ai/voices/clone/stream"

payload = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enable_text_normalization\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"language_code\"\r\n\r\nhi-IN\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_bitrate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_codec\"\r\n\r\nmp3\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"pace\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"speech_sample_rate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\nनमस्ते। यह एक स्ट्रीमिंग टेस्ट है।\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"voice_id\"\r\n\r\nsvc-123e4567-e89b-12d3-a456-426614174000\r\n-----011000010111000001101001--\r\n"
headers = {
    "api-subscription-key": "<apiSubscriptionKey>",
    "Content-Type": "multipart/form-data; boundary=---011000010111000001101001"
}

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

print(response.json())
```

```go
package main

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

func main() {

	url := "https://api.sarvam.ai/voices/clone/stream"

	payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enable_text_normalization\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"language_code\"\r\n\r\nhi-IN\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_bitrate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_codec\"\r\n\r\nmp3\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"pace\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"speech_sample_rate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\nनमस्ते। यह एक स्ट्रीमिंग टेस्ट है।\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"voice_id\"\r\n\r\nsvc-123e4567-e89b-12d3-a456-426614174000\r\n-----011000010111000001101001--\r\n")

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

	req.Header.Add("api-subscription-key", "<apiSubscriptionKey>")
	req.Header.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")

	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/voices/clone/stream")

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"] = 'multipart/form-data; boundary=---011000010111000001101001'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enable_text_normalization\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"language_code\"\r\n\r\nhi-IN\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_bitrate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_codec\"\r\n\r\nmp3\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"pace\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"speech_sample_rate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\nनमस्ते। यह एक स्ट्रीमिंग टेस्ट है।\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"voice_id\"\r\n\r\nsvc-123e4567-e89b-12d3-a456-426614174000\r\n-----011000010111000001101001--\r\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/voices/clone/stream")
  .header("api-subscription-key", "<apiSubscriptionKey>")
  .header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
  .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enable_text_normalization\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"language_code\"\r\n\r\nhi-IN\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_bitrate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_codec\"\r\n\r\nmp3\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"pace\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"speech_sample_rate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\nनमस्ते। यह एक स्ट्रीमिंग टेस्ट है।\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"voice_id\"\r\n\r\nsvc-123e4567-e89b-12d3-a456-426614174000\r\n-----011000010111000001101001--\r\n")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.sarvam.ai/voices/clone/stream', [
  'multipart' => [
    [
        'name' => 'language_code',
        'contents' => 'hi-IN'
    ],
    [
        'name' => 'output_audio_codec',
        'contents' => 'mp3'
    ],
    [
        'name' => 'text',
        'contents' => 'नमस्ते। यह एक स्ट्रीमिंग टेस्ट है।'
    ],
    [
        'name' => 'voice_id',
        'contents' => 'svc-123e4567-e89b-12d3-a456-426614174000'
    ]
  ]
  'headers' => [
    'api-subscription-key' => '<apiSubscriptionKey>',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://api.sarvam.ai/voices/clone/stream");
var request = new RestRequest(Method.POST);
request.AddHeader("api-subscription-key", "<apiSubscriptionKey>");
request.AddParameter("multipart/form-data; boundary=---011000010111000001101001", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enable_text_normalization\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"language_code\"\r\n\r\nhi-IN\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_bitrate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"output_audio_codec\"\r\n\r\nmp3\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"pace\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"speech_sample_rate\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\nनमस्ते। यह एक स्ट्रीमिंग टेस्ट है।\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"voice_id\"\r\n\r\nsvc-123e4567-e89b-12d3-a456-426614174000\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "api-subscription-key": "<apiSubscriptionKey>",
  "Content-Type": "multipart/form-data; boundary=---011000010111000001101001"
]
let parameters = [
  [
    "name": "enable_text_normalization",
    "value": 
  ],
  [
    "name": "language_code",
    "value": "hi-IN"
  ],
  [
    "name": "output_audio_bitrate",
    "value": 
  ],
  [
    "name": "output_audio_codec",
    "value": "mp3"
  ],
  [
    "name": "pace",
    "value": 
  ],
  [
    "name": "speech_sample_rate",
    "value": 
  ],
  [
    "name": "text",
    "value": "नमस्ते। यह एक स्ट्रीमिंग टेस्ट है।"
  ],
  [
    "name": "voice_id",
    "value": "svc-123e4567-e89b-12d3-a456-426614174000"
  ]
]

let boundary = "---011000010111000001101001"

var body = ""
var error: NSError? = nil
for param in parameters {
  let paramName = param["name"]!
  body += "--\(boundary)\r\n"
  body += "Content-Disposition:form-data; name=\"\(paramName)\""
  if let filename = param["fileName"] {
    let contentType = param["content-type"]!
    let fileContent = String(contentsOfFile: filename, encoding: String.Encoding.utf8)
    if (error != nil) {
      print(error as Any)
    }
    body += "; filename=\"\(filename)\"\r\n"
    body += "Content-Type: \(contentType)\r\n\r\n"
    body += fileContent
  } else if let paramValue = param["value"] {
    body += "\r\n\r\n\(paramValue)"
  }
}

let request = NSMutableURLRequest(url: NSURL(string: "https://api.sarvam.ai/voices/clone/stream")! 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()
```