---
title: Audio endpoints
description: POST /v1/audio/speech turns text into speech (OpenAI's request shape plus voice design, cloning, language and emotion), and /v1/audio/voices saves voice samples to clone — create, list, retrieve, rename, download and delete them.
layout: reference
---

Text to speech follows OpenAI's speech API, so the official SDKs work against `https://lovelyrouter.com/v1`. The guide, with the models, their voices and examples, is [Text to speech](/docs/models/speech). Authenticate with your API key as `Authorization: Bearer $LOVELY_API_KEY` ([Authentication](/docs/authentication)).

## Create speech {#create-speech}

::endpoint POST /v1/audio/speech

Speaks `input` and returns the audio file. The length in seconds is in the `x-lovely-audio-seconds` header and the call's id in `x-lovely-request-id`. Billed per second of audio made (at least one second); a request that fails is not charged.

:::params Body parameters
- `model` string, required
  `qwen3-tts` or `zonos2` (`GET /v1/models` lists them with their prices).
- `input` string, required
  The text to speak, up to 4,096 characters.
- `voice` string or object
  A preset voice of the model, an OpenAI voice name (`alloy`, `nova`, `onyx`, … map to a preset), or a saved voice's id (`voice_…`, also as `{"id": "voice_…"}`). Optional: without it the model picks a preset for `language`, or `qwen3-tts` designs a voice from `instructions`.
- `instructions` string
  `qwen3-tts` only, up to 1,000 characters. With a voice: how to speak (emotion, pace, volume, a whisper). Without one: a description of the voice to make.
- `language` string
  An ISO code (`en`, `zh`, `ja`, `ko`, `de`, `fr`, `pt-br`, …) or `auto` (default). Setting it helps with short texts and numbers.
- `response_format` string, default `mp3`
  `mp3`, `opus`, `aac`, `flac`, `wav` or `pcm` (16-bit, 24 kHz mono).
- `speed` number, default `1.0`
  0.25 to 4.0; the pitch stays the same.
- `emotion` string or object
  `zonos2` only: `happy`, `sad`, `angry` or `surprised`, or weights from 0 to 1 such as `{"happy": 0.7}`.
- `seed` integer
  For repeatable output, 0 to 2147483647.
- `reference_audio` string
  A one-off voice to clone: an `https` URL, a `data:` URL or base64 of a 3–15 s clip (WAV, MP3, M4A, OGG, FLAC or WebM, up to 10 MB). Not with `voice`.
- `reference_text` string
  What `reference_audio` says (`qwen3-tts` clones more closely with it).
- `consent` boolean
  Required with `reference_audio`: you confirm the voice is yours or you have the speaker's permission.
:::

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/audio/speech \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-tts",
    "input": "Hello! Nice to meet you.",
    "voice": "serena",
    "instructions": "Friendly and upbeat"
  }' \
  --output hello.mp3
```

```text title="Response headers" aside
HTTP/2 200
content-type: audio/mpeg
x-lovely-request-id: req_3f1c9a7e5b2d4c8e6a0f1b3d
x-lovely-audio-seconds: 2.16
```

Errors use the [usual shape](/docs/api/errors): `400 invalid_request_error` with `param` naming the field (an unknown voice, `instructions` on `zonos2`, `emotion` on `qwen3-tts`, a missing `consent`), `402` without balance, `404` for an unknown model or someone else's voice, `502` when the speech failed (nothing charged).

## The voice object {#the-voice-object}

:::params Attributes
- `id` string
  `voice_` followed by 24 characters. Use it as `voice`.
- `object` string
  Always `voice`.
- `name` string
  Up to 80 characters.
- `transcript` string | null
  What the sample says.
- `type` string
  The sample's format, such as `audio/wav`.
- `bytes` integer
  The sample's size.
- `audio_url` string
  Downloads the sample (with your API key).
- `created_at` integer
  Unix seconds.
:::

```json title="The voice object" aside
{
  "id": "voice_8f2c4a1e9b7d3f60a5c2e1d4",
  "object": "voice",
  "name": "Narrator",
  "transcript": "Thanks for listening. See you next week on the show.",
  "type": "audio/wav",
  "bytes": 482044,
  "audio_url": "https://lovelyrouter.com/v1/audio/voices/voice_8f2c4a1e9b7d3f60a5c2e1d4/audio",
  "created_at": 1792915200
}
```

## Save a voice {#save-a-voice}

::endpoint POST /v1/audio/voices

Saves a voice sample and returns `201` with the voice object. Send multipart (the sample as `file`) or JSON (`audio` as an `https` or `data:` URL). Free; up to 100 voices per account, kept until you delete them.

:::params Body parameters
- `name` string, required
  Up to 80 characters.
- `file` file (or `audio` string), required
  3 to 15 seconds of one person speaking clearly, without music: WAV, MP3, M4A, OGG/Opus, FLAC or WebM, up to 10 MB.
- `transcript` string
  What the sample says, up to 1,000 characters.
- `consent` boolean, required
  `true`: the voice is yours, or you have the speaker's permission to clone it.
:::

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/audio/voices \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -F name="Narrator" \
  -F transcript="Thanks for listening. See you next week on the show." \
  -F consent=true \
  -F file=@narrator.wav
```

## List voices {#list-voices}

::endpoint GET /v1/audio/voices

Your saved voices, newest first: `{"object": "list", "data": [voice, …]}`.

## Retrieve a voice {#retrieve-a-voice}

::endpoint GET /v1/audio/voices/{id}

One voice object. Another account's voice answers `404`.

## Download the sample {#download-the-sample}

::endpoint GET /v1/audio/voices/{id}/audio

The sample as you uploaded it.

## Update a voice {#update-a-voice}

::endpoint PATCH /v1/audio/voices/{id}

JSON with `name` and/or `transcript` (`null` clears it). Returns the voice object.

## Delete a voice {#delete-a-voice}

::endpoint DELETE /v1/audio/voices/{id}

Deletes the voice and its sample for good: `{"id": "voice_…", "object": "voice", "deleted": true}`.  Characters that used it no longer have a voice.
