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

# Text-to-speech

> Synthesize audio from text with one HTTP call.

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
POST https://gateway.llm-stats.com/v1/tts/synthesize
```

Sends raw audio bytes back in the response — no polling, no follow-up requests.

## Quickstart

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import requests

  response = requests.post(
      "https://gateway.llm-stats.com/v1/tts/synthesize",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
      json={
          "model_id": "eleven_v3",
          "text": "Hello, this is a test.",
          "format": "mp3",
          "sample_rate": 24000,
      },
  )

  with open("output.mp3", "wb") as f:
      f.write(response.content)
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const response = await fetch(
    "https://gateway.llm-stats.com/v1/tts/synthesize",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer YOUR_API_KEY",
      },
      body: JSON.stringify({
        model_id: "eleven_v3",
        text: "Hello, this is a test.",
        format: "mp3",
        sample_rate: 24000,
      }),
    },
  );

  const audioBlob = await response.blob();
  ```

  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl https://gateway.llm-stats.com/v1/tts/synthesize \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "model_id": "eleven_v3",
      "text": "Hello, this is a test.",
      "format": "mp3",
      "sample_rate": 24000
    }' \
    --output output.mp3
  ```
</CodeGroup>

## Request body

<ParamField body="model_id" type="string" required>
  TTS model ID (e.g. `eleven_v3`, `cartesia-sonic-2`).
</ParamField>

<ParamField body="text" type="string" required>
  Text to synthesize.
</ParamField>

<ParamField body="voice_id" type="string">
  Provider-specific voice ID. Falls back to the model's default voice when
  omitted.
</ParamField>

<ParamField body="format" type="&#x22;mp3&#x22; | &#x22;wav&#x22; | &#x22;opus&#x22; | &#x22;flac&#x22; | &#x22;aac&#x22;" default="&#x22;mp3&#x22;">
  Container / codec for the returned audio.
</ParamField>

<ParamField body="sample_rate" type="integer" default="24000">
  Output sample rate in Hz.
</ParamField>

<ParamField body="speed" type="number" default="1.0">
  Playback speed multiplier where supported.
</ParamField>

## Response

The body is the raw audio file. Inspect `Content-Type` to confirm the codec:

| `format` | `Content-Type` |
| -------- | -------------- |
| `mp3`    | `audio/mpeg`   |
| `wav`    | `audio/wav`    |
| `opus`   | `audio/opus`   |
| `flac`   | `audio/flac`   |
| `aac`    | `audio/aac`    |

## Streaming

For low-latency playback, open a WebSocket to `wss://gateway.llm-stats.com/v1/tts/stream` and follow the protocol described in the dashboard's [TTS playground](https://llm-stats.com). The HTTP endpoint above is the right choice for short, batch synthesis.

## Errors

Failures use the [shared error envelope](/gateway/errors). Common ones:

| Status | `error.code`           | When                                           |
| ------ | ---------------------- | ---------------------------------------------- |
| `400`  | `invalid_input`        | Missing `text`, unsupported `format`, …        |
| `401`  | `unauthenticated`      | Missing or invalid API key.                    |
| `402`  | `insufficient_quota`   | Out of credit.                                 |
| `429`  | `rate_limited`         | Quota exceeded — back off using `Retry-After`. |
| `502`  | `provider_unavailable` | Every TTS provider for this model errored.     |
