Input streaming
Send text over a WebSocket as it is produced and receive audio continuously.
When to use it
Use this API when the text is still being produced — typically an LLM’s token stream. Rather than buffering output into sentences and synthesizing one at a time, the server consumes fragments as they arrive and holds back only a few words: the model generates each audio chunk from everything already spoken plus a short lookahead, so it never needs a complete sentence.
- Speech starts about ten words into the stream, mid-sentence: 0.86 s to first audio versus 1.92 s for sentence buffering on the same simulated LLM stream
- Against a live LLM at typical API cadence, audio began ~450 ms after the model’s first token
- Prosody is continuous across chunk seams — each chunk is generated with the preceding audio as context, so there are no restarts or intonation resets at chunk boundaries
- The connection is opened once, not per utterance, so each reply skips DNS, TLS, and admission. Measured from an Indian client against production: first audio ~420 ms after connection open, token feed included
Two WebSocket endpoints expose this: a native one (recommended) and an ElevenLabs-compatible one for existing EL integrations. Both authenticate with your usual header on the upgrade request; see Authentication.
Native WebSocket
/v1/audio/speech/stream-inputConnect with query parameters, send text fragments as JSON messages the moment they’re produced, and read binary frames back: raw PCM, 16-bit LE mono at 24 kHz. The server chunks at safe boundaries and keeps rolling text-and-audio context, so chunk N+1 continues chunk N’s prosody. Don’t pre-chunk or sentence-split yourself.
| Parameter | Type | Default | Description |
|---|---|---|---|
| voice | query | - | Voice id from GET /v1/voices: an sv_-prefixed id, e.g. sv_enhdbrj5 (Aanya). |
| lang | query | - | Language hint: any form works (hi, hin, hindi, hi-IN). Omit to auto-detect from script. |
| mode | query | sentence | Chunking strategy: sentence (waits for sentence boundaries) or eager (word-level, lowest latency; see below). |
| speed | query | 1.0 | Speaking speed, 0.7–1.5, pitch-preserving. Applied continuously across the whole session, so chunk seams stay seamless. |
| temperature … | query | - | Sampling knobs ride as query params too. Omit them; the server fills serving-tuned defaults. |
Messages you send
| Parameter | Type | Default | Description |
|---|---|---|---|
| {"text": "fragment "} | JSON | - | A text fragment, as small as a single token delta. Send them as fast as they arrive. |
| {"flush": true} | JSON | - | Force everything buffered out as audio now (for example at the end of an LLM paragraph). |
| {"text": ""} | JSON | - | End of stream. The server synthesizes the remainder, sends {"type": "done"}, and closes. |
pip install git+https://github.com/kenpath-labs/svara-python.gitEager mode
mode=eager is the lowest-latency configuration and maps directly onto how the model was trained. The server buffers incoming deltas at word granularity (mid-word token deltas are handled), and as soon as chunk_words + peek_words complete words exist it synthesizes the first chunk_words with the next few words attached as the lookahead peek:
| Parameter | Type | Default | Description |
|---|---|---|---|
| chunk_words | query | 4 | Words per synthesis chunk. Smaller = earlier first audio, more chunk seams (they’re seamless, but each chunk has scheduling overhead). |
| peek_words | query | 2 | Lookahead words held back so each chunk knows what’s coming (clamped 1–5). The final chunk goes out with no peek, which is the model’s end-of-utterance signal. |
| max_chunk_words | query | 20 | Upper bound on a chunk once text has queued up — keeps a fast LLM from producing one enormous chunk. |
With defaults, speech starts roughly 6 words into the LLM’s output; typically well before the first sentence ends.
ElevenLabs-compatible WebSocket
/v1/text-to-speech/{voice_id}/stream-inputA drop-in implementation of the ElevenLabs realtime protocol, for existing EL integrations: the official EL SDKs’ WebSocket clients work unmodified. The full protocol is supported:
- BOS:
{"text": " ", "generation_config": {"chunk_length_schedule": [120,160,250,290]}}(schedule values clamped 50-500;auto_modesupported) - Text:
{"text": "fragment ", "try_trigger_generation": true}, plus{"flush": true}; a bare space is a keepalive - EOS:
{"text": ""} - Audio frames:
{"audio": "<base64>", "alignment": …, "normalizedAlignment": …}in the negotiatedoutput_format(query param, e.g.pcm_24000,mp3_44100_128), then{"isFinal": true}and a clean close (code 1000) inactivity_timeoutquery param: 20 s default, 180 s max
Character timings in alignment are chunk-relative and approximate: chunk boundaries are sample-exact, characters uniform within a chunk.
Tips
- Forward raw deltas. Don’t sentence-split, batch, or “clean up” the LLM stream client-side; the server’s chunker is the one that knows the model’s trained format.
- Buffer ~150 ms on playback. A small jitter buffer on your audio sink absorbs network variance without hurting perceived latency.
- Keep the socket warm for turns, not sessions. Open the connection when a reply starts, close on
{"type": "done"}. Each connection is one utterance with one prosodic arc. - Concurrent conversations need concurrent streams: each open socket counts against your plan’s concurrency limit (see Rate limits).