Skip to main content
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

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:
PropertyValue
Content-Typeaudio/L16
EncodingSigned 16-bit PCM, little-endian
Sample rate44100 Hz
Channels1 (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

HeaderDescription
X-inference-idUnique id for the generation
X-audio-urlURL of the assembled WAV (available after the stream finishes)
Credit-UtilizedCredits charged for the request

Consuming the stream

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

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.
For token-by-token input or sessions longer than a single 500-character utterance, use the WebSocket API instead.