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

# Live Speech (WebSocket)

> Send text as it is produced and get audio frames back.

## Overview

Send text as your LLM produces it and get audio frames back as they render, over one WebSocket connection.

Use this when you do not have the full text up front. If you do, use [Synthesize Speech](/api-v2/post/tts) — same voices, same controls.

<Warning>
  This endpoint is **not yet enabled on the public API**. A connection to
  `wss://api.bland.ai/v2/tts/live` currently fails the WebSocket handshake. Use
  [Synthesize Speech](/api-v2/post/tts) until this page drops the notice.
</Warning>

<Note>
  Use a **`BTTS_V3` or newer** voice. `expressiveness` and `stability` are calibrated for
  it. `BTTS_V2` voices synthesize, but the controls are not tuned for them.
</Note>

***

## Authentication

Credentials are checked in this order:

* `?token=<JWT>` query parameter (recommended for browsers). Mint via [Mint Stream Input Token](/api-v1/post/speak-stream-input-token).
* `Authorization: Bearer <api_key>` header (server-side).
* `Sec-WebSocket-Protocol: bland.api_key.<key>` subprotocol.
* `?api_key=<key>` query (deprecated, logged).

***

## Protocol

Control frames are JSON text. Audio frames are **binary**, so tell them apart by WebSocket frame type. There is no `container` here — you always get bare frames in the encoding you asked for.

### Client → Server messages

```
init   { type: "init", voice, audio?: { encoding?, sample_rate? },
         controls?: { expressiveness?, stability? },
         auto_flush?, auto_flush_char_threshold? }
speak  { type: "speak", text, flush? }
flush  { type: "flush" }
close  { type: "close" }
```

* `init` is required as the first message. `voice` is a UUID (names are rejected).
  * `audio.encoding` defaults to `pcm_s16le`. `mulaw` is 8 kHz only, for telephony.
  * `audio.sample_rate` defaults to `48000`, the rate BTTS V3 renders natively. Allowed: `8000`, `16000`, `24000`, `44100`, `48000`.
  * `controls.expressiveness` and `controls.stability` are optional `0.0`–`1.0`.
  * `auto_flush` (default `true`) triggers synthesis on sentence boundaries or when the buffer reaches `auto_flush_char_threshold`.
* `speak` appends text. Setting `flush: true` renders the buffer immediately.
* `flush` renders whatever is buffered.
* `close` renders any remaining text, sends `done`, then closes.

### Server → Client messages

```
ready  { type: "ready", session_id, model, encoding, sample_rate }
<binary audio frame>
done   { type: "done", session_id, characters_billed, cost_usd, latency_ms }
error  { type: "error", code, message }
```

### Limits and guardrails

```
Buffer cap:    4000 chars before a flush
Speak rate:    500 messages / 10s window
Idle timeout:  60s (no client messages → close)
Slow consumer: session is closed if the client falls behind the audio stream
Billing:       once, on close or disconnect — never per flush
```

### Error codes

| Code                      | Meaning                                                 |
| ------------------------- | ------------------------------------------------------- |
| `invalid_message`         | Frame is not valid JSON or is not a known message type. |
| `init_required`           | A non-`close` message arrived before `init`.            |
| `already_initialized`     | `init` was sent more than once.                         |
| `invalid_request`         | A field is missing or has the wrong shape.              |
| `voice_not_found`         | Voice UUID does not exist or is not accessible.         |
| `unsupported_voice`       | Voice is not a `BTTS_V2` or `BTTS_V3` voice.            |
| `unsupported_encoding`    | `audio.encoding` is not allowed.                        |
| `unsupported_sample_rate` | `audio.sample_rate` is not allowed for the encoding.    |
| `buffer_overflow`         | Un-flushed text would exceed the buffer cap.            |
| `rate_limited`            | Too many `speak` messages in the rolling window.        |
| `idle_timeout`            | Session exceeded the idle timeout.                      |
| `slow_consumer`           | Client is not reading audio fast enough.                |
| `synthesis_failed`        | Synthesis failed.                                       |

***

## Example

```js Node (ws) theme={null}
import WebSocket from "ws";

const ws = new WebSocket("wss://api.bland.ai/v2/tts/live", {
  headers: { Authorization: `Bearer ${process.env.BLAND_API_KEY}` },
});

ws.on("open", () => {
  ws.send(JSON.stringify({
    type: "init",
    voice: "f04af0e5-1a80-48a9-b02d-52f30d417cfa",
    audio: { encoding: "pcm_s16le", sample_rate: 48000 },
    controls: { expressiveness: 0.6, stability: 0.5 },
  }));
});

ws.on("message", (data, isBinary) => {
  if (isBinary) {
    // Play or forward the raw audio frame.
    return;
  }
  const msg = JSON.parse(data.toString());
  if (msg.type === "ready") {
    ws.send(JSON.stringify({ type: "speak", text: "Hello, " }));
    ws.send(JSON.stringify({ type: "speak", text: "how can I help you today?", flush: true }));
    ws.send(JSON.stringify({ type: "close" }));
  }
  if (msg.type === "done") console.log("Billed:", msg.characters_billed, "cost:", msg.cost_usd);
  if (msg.type === "error") console.error(msg.code, msg.message);
});
```

***

Docs for agents: [llms.txt](/llms.txt)
