# ElevenLabs

URL: https://docs.hanzo.ai/docs/guides/migrate/elevenlabs

> ElevenLabs synthesizes speech and transcribes it back. Here that is /v1/audio — five operations, OpenAI-shaped for speech and transcription.

ElevenLabs turns text into speech, and audio back into text, with sound effects
and music beside it. `/v1/audio` (5 operations) covers the same four verbs:
`speech` and `transcriptions` in the OpenAI shape, and `voice`, `music` and
`foley` for the Zen family's own.

## Start here

Two calls after the key: turn a sentence into an MP3, then turn the MP3 back
into the sentence.

```bash
# 1. mint a key — sk- belongs on a server, pk- is safe in a browser
curl -sS -X POST https://api.hanzo.ai/v1/account/keys \
  -H "Authorization: Bearer $HANZO_SESSION" \
  -H 'Content-Type: application/json' \
  -d '{"type":"secret"}'

# 2. synthesize — the bytes stream back, so write them to a file
curl -sS -X POST https://api.hanzo.ai/v1/audio/speech \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"qwen3-tts-voicedesign","input":"The first move is what sets everything in motion.","response_format":"mp3"}' \
  -o out.mp3

# 3. read it back — multipart, file and model
curl -sS -X POST https://api.hanzo.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -F file=@out.mp3 \
  -F model=whisper-1
```

Step 3 returns the sentence from step 2. The round trip proves the key carries,
that `model` resolved to a provider in both directions, and that both halves are
OpenAI-shaped — so an OpenAI client pointed at `https://api.hanzo.ai/v1` reaches
them with no other change.

### Core capabilities

| Capability    | What it does                                                                        | Operations |
| ------------- | ----------------------------------------------------------------------------------- | ---------- |
| `/v1/audio`   | Speech from text, text from speech, and the Zen family's own voice, music and foley | 5          |
| `/v1/models`  | The ids this gateway resolves right now, per modality                               | 4          |
| `/v1/account` | Mints, lists and revokes keys — `/v1/account/keys` issued the `sk-` in step 1       | 10         |

## Nouns

| ElevenLabs                                  | Hanzo                                                                                                 |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `client.text_to_speech.convert(...)`        | `POST /v1/audio/speech` — `model`, `input`, optional `voice`                                          |
| `POST .../text-to-speech/{voice_id}`        | The same route; the voice is a body field, not a path segment                                         |
| `model_id`, e.g. `eleven_v3`                | `model`, e.g. `qwen3-tts-voicedesign`                                                                 |
| `output_format`, e.g. `mp3_44100_128`       | `response_format` — `mp3` by default                                                                  |
| `client.speech_to_text.convert(...)`        | `POST /v1/audio/transcriptions` — multipart `file` and `model`, with `language` and `response_format` |
| `client.text_to_sound_effects.convert(...)` | `POST /v1/audio/foley`                                                                                |
| `client.music.compose(...)`                 | `POST /v1/audio/music`                                                                                |
| The Zen-native speech verb                  | `POST /v1/audio/voice`, with `zen-voice`                                                              |
| `xi-api-key` header                         | `Authorization: Bearer $HANZO_API_KEY`                                                                |

`model` resolves to its provider through the same routing every other modality
uses, so a node you registered as a TTS provider is reached the same way a hosted
one is.

## The call

ElevenLabs:

```python
from elevenlabs.client import ElevenLabs

elevenlabs = ElevenLabs()   # ELEVENLABS_API_KEY in the environment

audio = elevenlabs.text_to_speech.convert(
    text="The first move is what sets everything in motion.",
    voice_id="JBFqnCBsd6RMkjVDRZzb",
    model_id="eleven_v3",
    output_format="mp3_44100_128",
)
with open("out.mp3", "wb") as f:
    for chunk in audio:
        f.write(chunk)
```

Hanzo — the bytes stream straight back, so write them to a file:

```bash
curl -X POST https://api.hanzo.ai/v1/audio/speech \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen3-tts-voicedesign",
    "input": "The first move is what sets everything in motion.",
    "response_format": "mp3"
  }' \
  -o out.mp3
```

`model` and `input` are the only required fields. `voice` is optional and is
carried to the provider when set. Because the body is OpenAI's, an OpenAI client
pointed at `https://api.hanzo.ai/v1` reaches this endpoint with no other change.

Transcription is the mirror of it — multipart, `file` and `model`, where the
model is one of the whisper family:

```bash
curl -X POST https://api.hanzo.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -F file=@out.mp3 \
  -F model=whisper-1
```

Read `GET /v1/models` for the ids this gateway currently resolves for each
modality, rather than hardcoding one.

## What does not carry

**No voice library.** ElevenLabs' catalogue of voices, `voices.search()`,
per-voice settings, voice design and instant or professional cloning have no
equivalent. `voice` is an optional string carried to the provider; there is no
route that lists what it accepts, and there is nothing that creates a voice.

**Only two of the five operations are shaped like OpenAI's.** `speech` and
`transcriptions` are. `voice`, `music` and `foley` are Zen-native verbs and
reject a non-Zen model, so they are not a drop-in for an ElevenLabs client.

**Speech input is capped at 4096 characters.** Longer text is refused rather
than truncated. Chunk it yourself.

**No word-level timestamps, no alignment, no dubbing, no voice changer.**
`convert_with_timestamps`, the dubbing API and the speech-to-speech endpoints
have nothing on this side.

**A publishable key is refused here.** `/v1/audio/speech` requires a secret key.
That is not a general rule about the platform — it is this endpoint, because it
synthesizes billable audio, and a publishable key lives in browsers.

**No real-time socket.** ElevenLabs streams synthesis over a WebSocket for
conversational latency. This is a request and a response, with the audio bytes
streamed back on it.
