curl https://api.snapgen.org/v1/audio/speech \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "eleven-v4",
"input": "Welcome back! Today we explore the northern lights.",
"voice": "JBFqnCBsd6RMkjVDRZzb",
"response_format": "mp3"
}' \
--output speech.mp3
import json
import os
from urllib.request import Request, urlopen
request = Request(
"https://api.snapgen.org/v1/audio/speech",
data=json.dumps({
"model": "eleven-v4",
"input": "Welcome back! Today we explore the northern lights.",
"voice": "JBFqnCBsd6RMkjVDRZzb",
"response_format": "mp3",
}).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response, open("speech.mp3", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.snapgen.org/v1/audio/speech", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "eleven-v4",
input: "Welcome back! Today we explore the northern lights.",
voice: "JBFqnCBsd6RMkjVDRZzb",
response_format: "mp3",
}),
});
if (!response.ok) throw new Error(await response.text());
await writeFile("speech.mp3", Buffer.from(await response.arrayBuffer()));
HTTP/1.1 200 OK
content-type: audio/mpeg
cache-control: no-store
x-gateway-execution-id: 5b1e9f3a-2c4d-4e6f-8a7b-9c0d1e2f3a4b
<binary MP3 audio>
{
"error": {
"message": "voice: must be one of the listed voice IDs or names",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request"
}
}
Endpoint Reference
Text to speech
Turn text into speech with ElevenLabs voices through the OpenAI-compatible speech endpoint.
POST
/
v1
/
audio
/
speech
curl https://api.snapgen.org/v1/audio/speech \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "eleven-v4",
"input": "Welcome back! Today we explore the northern lights.",
"voice": "JBFqnCBsd6RMkjVDRZzb",
"response_format": "mp3"
}' \
--output speech.mp3
import json
import os
from urllib.request import Request, urlopen
request = Request(
"https://api.snapgen.org/v1/audio/speech",
data=json.dumps({
"model": "eleven-v4",
"input": "Welcome back! Today we explore the northern lights.",
"voice": "JBFqnCBsd6RMkjVDRZzb",
"response_format": "mp3",
}).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response, open("speech.mp3", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.snapgen.org/v1/audio/speech", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "eleven-v4",
input: "Welcome back! Today we explore the northern lights.",
voice: "JBFqnCBsd6RMkjVDRZzb",
response_format: "mp3",
}),
});
if (!response.ok) throw new Error(await response.text());
await writeFile("speech.mp3", Buffer.from(await response.arrayBuffer()));
HTTP/1.1 200 OK
content-type: audio/mpeg
cache-control: no-store
x-gateway-execution-id: 5b1e9f3a-2c4d-4e6f-8a7b-9c0d1e2f3a4b
<binary MP3 audio>
{
"error": {
"message": "voice: must be one of the listed voice IDs or names",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request"
}
}
POST /v1/audio/speech turns text into speech and returns the audio file as the response body. It accepts the OpenAI speech request (model, input, voice, response_format, and speed) and adds ElevenLabs voice settings. Choose a model from Audio Series > ElevenLabs Speech.
| Model | Characters per request | Price |
|---|---|---|
eleven-v4 | Up to 10,000 | $0.12 per 1,000 characters |
eleven-v3 | Up to 5,000 | $0.12 per 1,000 characters |
eleven-multilingual-v2 | Up to 10,000 | $0.12 per 1,000 characters |
eleven-flash-v2.5 | Up to 40,000 | $0.06 per 1,000 characters |
string
required
One of the speech models above.
string
required
The text to speak, up to the model’s character limit. Newlines are allowed;
blank text and other control characters are rejected. Each character is
billed.
string
required
A voice ID or first name from the voice list, for example
JBFqnCBsd6RMkjVDRZzb or George. Names are case-insensitive. OpenAI voice
names such as alloy aren’t available.string
default:"mp3"
mp3, opus, pcm, or wav. See Output formats.number
Speaking speed, from
0.7 to 1.2.string
An ISO 639 language code of two or three lowercase letters, such as
en or
de. The provider rejects a code the model doesn’t support.number
From
0 to 1. Lower values give a broader emotional range; higher values
sound more consistent from one generation to the next.number
From
0 to 1. How closely the output follows the original voice.number
From
0 to 1. Exaggerates the voice’s speaking style.boolean
Boosts similarity to the original speaker.
integer
From
0 to 4294967295. Makes repeated requests more repeatable, without
guaranteeing identical audio.string
Up to 5,000 characters that come before
input. Send it when you split long
text across requests so the speech flows across the join. Not billed.string
Up to 5,000 characters that follow
input, for the same purpose. Not billed.string
auto, on, or off: whether numbers, dates, and similar text are spelled
out before they’re spoken.instructions, returns invalid_request.
Response
A successful request returns HTTP200 with the audio as the body. The gateway
streams it to you as the provider sends it; save the body to a file.
| Header | Value |
|---|---|
Content-Type | The type of the requested response_format, for example audio/mpeg |
Cache-Control | no-store |
x-gateway-execution-id | The execution ID. Quote it when you contact support. |
Output formats
response_format | Encoding | Content-Type |
|---|---|---|
mp3 (default) | MP3, 44.1 kHz, 128 kbps | audio/mpeg |
opus | Opus, 48 kHz, 128 kbps | audio/ogg |
pcm | Raw 16-bit PCM, 24 kHz, no header | audio/pcm |
wav | WAV, 44.1 kHz | audio/wav |
Voices
Every speech, dialogue, and voice changer model uses the same 21 ElevenLabs premade voices. Send the voice ID or the first name asvoice. Other
ElevenLabs voices, such as cloned or library voices, aren’t available.
| Name | voice ID | Gender | Accent | Description | Preview |
|---|---|---|---|---|---|
| Adam | pNInz6obpgDQGcFmaJgB | Male | American | Dominant, Firm | Listen |
| Alice | Xb7hH8MSUJpSbSDYk0k2 | Female | British | Clear, Engaging Educator | Listen |
| Bella | hpp4J3VqNfWAUOO0d1Us | Female | American | Professional, Bright, Warm | Listen |
| Bill | pqHfZKP75CvOlQylNhV4 | Male | American | Wise, Mature, Balanced | Listen |
| Brian | nPczCjzI2devNBz1zQrb | Male | American | Deep, Resonant and Comforting | |
| Callum | N2lVS1w4EtoT3dr4eOWO | Male | American | Husky Trickster | Listen |
| Charlie | IKne3meq5aSn9XLyUdCD | Male | Australian | Deep, Confident, Energetic | |
| Chris | iP95p4xoKVk53GoZ742B | Male | American | Charming, Down-to-Earth | Listen |
| Daniel | onwK4e9ZLuTAKqWW03F9 | Male | British | Steady Broadcaster | |
| Eric | cjVigY5qzO86Huf0OWal | Male | American | Smooth, Trustworthy | Listen |
| George | JBFqnCBsd6RMkjVDRZzb | Male | British | Warm, Captivating Storyteller | |
| Harry | SOYHLrjzK2X1ezoPC6cr | Male | American | Fierce Warrior | Listen |
| Jessica | cgSgspJ2msm6clMCkdW9 | Female | American | Playful, Bright, Warm | Listen |
| Laura | FGY2WhTYpPnrIDTdsKH5 | Female | American | Enthusiast, Quirky Attitude | |
| Liam | TX3LPaxmHKxFdv7VOQHJ | Male | American | Energetic, Social Media Creator | Listen |
| Lily | pFZP5JQG7iQjIQuC4Bku | Female | British | Velvety Actress | Listen |
| Matilda | XrExE9yKIg1WjnnlVkGX | Female | American | Knowledgable, Professional | Listen |
| River | SAz9YHcvj6GT2YYXdXww | Neutral | American | Relaxed, Neutral, Informative | Listen |
| Roger | CwhRBWXzGAHq8TQ4Fs17 | Male | American | Laid-Back, Casual, Resonant | Listen |
| Sarah | EXAVITQu4vr4xnSDxMaL | Female | American | Mature, Reassuring, Confident | Listen |
| Will | bIHbv24MWmeRgasZH58o | Male | American | Relaxed Optimist | Listen |
Use the OpenAI SDK
The official OpenAI SDKs work with the SnapGen base URL. Pass a voice from the list above. In Python, send ElevenLabs fields such asstability through
extra_body.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["SNAPGEN_API_KEY"],
base_url="https://api.snapgen.org/v1",
)
with client.audio.speech.with_streaming_response.create(
model="eleven-v4",
voice="George",
input="Welcome back! Today we explore the northern lights.",
response_format="mp3",
extra_body={"stability": 0.5},
) as response:
response.stream_to_file("speech.mp3")
const fs = require("node:fs");
const OpenAI = require("openai");
async function main() {
const client = new OpenAI({
apiKey: process.env.SNAPGEN_API_KEY,
baseURL: "https://api.snapgen.org/v1",
});
const response = await client.audio.speech.create({
model: "eleven-v4",
voice: "George",
input: "Welcome back! Today we explore the northern lights.",
response_format: "mp3",
});
fs.writeFileSync("speech.mp3", Buffer.from(await response.arrayBuffer()));
}
main();
Billing
The request is billed per character ofinput, counted in Unicode code points:
Héllo 👋 is 7 characters. previous_text and next_text are free. The
gateway prices the request before it calls the provider, so the charge is
known up front, and a failed request isn’t charged. For example, 1,000
characters cost $0.12 on eleven-v4 and $0.06 on eleven-flash-v2.5.
This endpoint is synchronous and rejects
Idempotency-Key. If a request
times out, the audio may already have been generated: check your Console
request logs before you send it again.Errors
Errors use the standard error envelope.| Status | error.code | Cause |
|---|---|---|
| 400 | invalid_request | A field is unknown or out of range, or voice isn’t in the voice list. The message names the field. |
| 400 | invalid_input | input is missing or empty. |
| 400 | input_too_long | input has more characters than the model accepts. |
| 400 | invalid_content_type | The body isn’t sent as application/json. |
| 400 | idempotency_not_supported | The request has an Idempotency-Key header. |
| 400 | provider_rejected_request | The provider refused the request, for example a language the model doesn’t support. |
| 402 | insufficient_funds | Your balance can’t cover the request. |
| 404 | model_not_available | The model ID is unknown or disabled. A key whose allowlist excludes the model gets api_key_model_not_allowed (403). |
| 429 | rate_limit_exceeded, provider_rate_limited | Too many requests. Wait for Retry-After, then back off. |
| 502 | provider_http_error, provider_timeout | The provider failed. Retry later. |
| 503 | no_eligible_route, provider_unavailable | The model doesn’t run on this endpoint, or no route is available right now. |
curl https://api.snapgen.org/v1/audio/speech \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "eleven-v4",
"input": "Welcome back! Today we explore the northern lights.",
"voice": "JBFqnCBsd6RMkjVDRZzb",
"response_format": "mp3"
}' \
--output speech.mp3
import json
import os
from urllib.request import Request, urlopen
request = Request(
"https://api.snapgen.org/v1/audio/speech",
data=json.dumps({
"model": "eleven-v4",
"input": "Welcome back! Today we explore the northern lights.",
"voice": "JBFqnCBsd6RMkjVDRZzb",
"response_format": "mp3",
}).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response, open("speech.mp3", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.snapgen.org/v1/audio/speech", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "eleven-v4",
input: "Welcome back! Today we explore the northern lights.",
voice: "JBFqnCBsd6RMkjVDRZzb",
response_format: "mp3",
}),
});
if (!response.ok) throw new Error(await response.text());
await writeFile("speech.mp3", Buffer.from(await response.arrayBuffer()));
HTTP/1.1 200 OK
content-type: audio/mpeg
cache-control: no-store
x-gateway-execution-id: 5b1e9f3a-2c4d-4e6f-8a7b-9c0d1e2f3a4b
<binary MP3 audio>
{
"error": {
"message": "voice: must be one of the listed voice IDs or names",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request"
}
}