> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chariot.in/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP streaming

> Stream text-to-speech audio over HTTP for low time-to-first-byte.

`POST /v1/tts/stream` synthesizes speech and streams the audio back as it is generated, so you can begin playback before the whole utterance is ready. It takes the same request body as the synchronous endpoint.

## Request

```bash theme={null}
curl -N -X POST https://api.chariot.in/v1/tts/stream \
  -H "chariotai-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "voice_id": "bac7d666-094d-4698-91fa-741d60fce662",
    "text": "This audio starts playing before it has finished generating."
  }' \
  --output speech.pcm
```

## Response format

The body is a chunked stream of **raw audio**, not a container file:

| Property     | Value                            |
| ------------ | -------------------------------- |
| Content-Type | `audio/L16`                      |
| Encoding     | Signed 16-bit PCM, little-endian |
| Sample rate  | 44100 Hz                         |
| Channels     | 1 (mono)                         |

There is **no WAV/RIFF header** on the stream. To play it, point your audio sink at a 44.1 kHz mono s16le source, or prepend your own WAV header. The fully assembled WAV is uploaded to storage and its URL is returned in the `X-audio-url` response header once the stream completes.

### Response headers

| Header            | Description                                                    |
| ----------------- | -------------------------------------------------------------- |
| `X-inference-id`  | Unique id for the generation                                   |
| `X-audio-url`     | URL of the assembled WAV (available after the stream finishes) |
| `Credit-Utilized` | Credits charged for the request                                |

## Consuming the stream

<CodeGroup>
  ```python Python theme={null}
  import requests

  with requests.post(
      "https://api.chariot.in/v1/tts/stream",
      headers={"chariotai-api-key": "YOUR_API_KEY"},
      json={"voice_id": "bac7d666-094d-4698-91fa-741d60fce662",
            "text": "Streaming keeps latency low."},
      stream=True,
  ) as r:
      r.raise_for_status()
      print("inference:", r.headers["X-inference-id"])
      for chunk in r.iter_content(chunk_size=4096):
          if chunk:
              play_pcm(chunk)  # 44.1 kHz, mono, s16le
  ```

  ```javascript Node.js theme={null}
  const res = await fetch("https://api.chariot.in/v1/tts/stream", {
    method: "POST",
    headers: {
      "chariotai-api-key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      voice_id: "bac7d666-094d-4698-91fa-741d60fce662",
      text: "Streaming keeps latency low.",
    }),
  });
  if (!res.ok) throw new Error(await res.text());

  for await (const chunk of res.body) {
    playPcm(chunk); // 44.1 kHz, mono, s16le
  }
  ```
</CodeGroup>

## Error handling

Because the HTTP status line is sent **before** synthesis begins, a `200` only means the request was accepted, not that the whole stream succeeded. If generation fails partway, the stream simply ends early. Guard against truncation:

* Track how much audio you received and treat an unexpectedly short stream as a failure.
* Any credits charged for a failed generation are refunded automatically.

Validation and auth errors (bad `voice_id`, text over 500 characters, missing key) happen before streaming starts and return a normal JSON error with the appropriate status code. See [Errors & status codes](/guides/resources/errors).

<Note>
  For token-by-token input or sessions longer than a single 500-character utterance, use the [WebSocket API](/guides/text-to-speech/websocket) instead.
</Note>
