Skip to main content
ElevenLabs Documentation Docs
current

Search documentation

Type to search this documentation.

On this pageOverview

Stream dialogue in real-time

Stream dialogue in real-time

The Text to Dialogue WebSocket (/v1/text-to-dialogue/stream-input) keeps a single connection open while you send dialogue lines and receive base64-encoded audio chunks. It is intended for Eleven v3 and Eleven v4 dialogue models only (model_id must start with eleven_v3 or eleven_v4).

  • An ElevenLabs account with an API key (authentication).
  • The API key must have Text to Speech permissions.
  • Python or Node.js installed on your machine.
Python
pip install python-dotenv websockets

Create a .env file:

.env

Bash
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here

Pick a voice ID from the Voice Library. The examples below use eleven_v4_turbo, which allows one registered voice per connection.

Connect to wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input with query parameters such as model_id and output_format. You can send the API key in the xi-api-key header or in the first JSON message (shown here in the body for a single pattern across languages).

Python
import asyncio
import base64
import json
import os

from dotenv import load_dotenv
import websockets

load_dotenv()
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
VOICE_ID = "21m00Tcm4TlvDq8ikWAM"
MODEL_ID = "eleven_v4_turbo"

URI = (
    "wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input"
    f"?model_id={MODEL_ID}&output_format=mp3_44100_128"
)

Send a first message that includes voices (required) and xi_api_key if you did not set the xi-api-key header. Then send one or more frames with inputs: each item has text, voice_id, and optional new_turn.

The server buffers text until it has enough context (about 40 characters and 8 words), then emits audio chunks. Response fields use snake_case (for example is_final).

Python
async def stream_dialogue():
    async with websockets.connect(URI) as websocket:
        await websocket.send(
            json.dumps(
                {
                    "voices": [VOICE_ID],
                    "xi_api_key": ELEVENLABS_API_KEY,
                }
            )
        )

        line = (
            "This is a longer line of dialogue used to exceed the minimum buffer so the model "
            "starts generating streamed audio for the registered voice. "
        )
        await websocket.send(
            json.dumps(
                {
                    "inputs": [
                        {"text": line, "voice_id": VOICE_ID, "new_turn": False},
                    ],
                }
            )
        )

        await websocket.send(json.dumps({"close_socket": True}))

        os.makedirs("output", exist_ok=True)
        out_path = "output/dialogue-ws.mp3"
        with open(out_path, "wb") as audio_file:
            while True:
                raw = await websocket.recv()
                msg = json.loads(raw)
                if msg.get("error"):
                    raise RuntimeError(msg)
                if msg.get("audio"):
                    audio_file.write(base64.b64decode(msg["audio"]))
                if msg.get("is_final"):
                    break
        print(f"Wrote {out_path}")

asyncio.run(stream_dialogue())

close_socket flushes any buffered text, sends remaining audio, then a final frame with is_final: true before the connection closes. To keep the connection open between lines, omit close_socket until the session ends; use flush to force audio for shorter buffers without closing.

Python
python text-to-dialogue-websocket.py

You should get an MP3 file under output/ (filename as in the example above).

Unlike the TTS WebSocket chunk_length_schedule, dialogue streaming uses a fixed server threshold (character and word count) before the first partial audio. If you send short lines and hear delays, batch slightly more text per inputs frame or send flush: true to force generation without closing the socket.

Set new_turn: true when a speaker finishes a turn so prosody resets cleanly. Changing voice_id between inputs entries also starts a new turn. With eleven_v4_turbo, register exactly one voice in voices; eleven_v4 supports up to 10 registered voices.

If the server receives no client message for 20 seconds, the connection ends. Send {"keep_alive": true} to reset the timer without synthesizing audio.

Each open connection holds one dialogue session for as long as it stays open, drawn from a dedicated pool separate from your plan’s standard concurrency limit. Audio generated over the connection does not count toward standard concurrency. See Text to Dialogue concurrency.

Add sync_alignment=true to the query string to receive alignment objects (snake_case timing arrays) on chunks when available. See the API reference.

Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu