curl https://api.snapgen.org/v1/audio/dialogue \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "eleven-v4-dialogue",
"inputs": [
{
"text": "[excited] Did you see the launch?",
"voice": "JBFqnCBsd6RMkjVDRZzb"
},
{
"text": "[laughs] I watched it twice!",
"voice": "EXAVITQu4vr4xnSDxMaL"
}
]
}' \
--output dialogue.mp3
import json
import os
from urllib.request import Request, urlopen
body = {
"model": "eleven-v4-dialogue",
"inputs": [
{"text": "[excited] Did you see the launch?", "voice": "JBFqnCBsd6RMkjVDRZzb"},
{"text": "[laughs] I watched it twice!", "voice": "EXAVITQu4vr4xnSDxMaL"},
],
}
request = Request(
"https://api.snapgen.org/v1/audio/dialogue",
data=json.dumps(body).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response, open("dialogue.mp3", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.snapgen.org/v1/audio/dialogue", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "eleven-v4-dialogue",
inputs: [
{ text: "[excited] Did you see the launch?", voice: "JBFqnCBsd6RMkjVDRZzb" },
{ text: "[laughs] I watched it twice!", voice: "EXAVITQu4vr4xnSDxMaL" },
],
}),
});
if (!response.ok) throw new Error(await response.text());
await writeFile("dialogue.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": "inputs: dialogue text must total at most 2000 characters",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request"
}
}
Endpoint Reference
Text to dialogue
Generate one audio file of a conversation in which each line has its own ElevenLabs voice.
POST
/
v1
/
audio
/
dialogue
curl https://api.snapgen.org/v1/audio/dialogue \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "eleven-v4-dialogue",
"inputs": [
{
"text": "[excited] Did you see the launch?",
"voice": "JBFqnCBsd6RMkjVDRZzb"
},
{
"text": "[laughs] I watched it twice!",
"voice": "EXAVITQu4vr4xnSDxMaL"
}
]
}' \
--output dialogue.mp3
import json
import os
from urllib.request import Request, urlopen
body = {
"model": "eleven-v4-dialogue",
"inputs": [
{"text": "[excited] Did you see the launch?", "voice": "JBFqnCBsd6RMkjVDRZzb"},
{"text": "[laughs] I watched it twice!", "voice": "EXAVITQu4vr4xnSDxMaL"},
],
}
request = Request(
"https://api.snapgen.org/v1/audio/dialogue",
data=json.dumps(body).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response, open("dialogue.mp3", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.snapgen.org/v1/audio/dialogue", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "eleven-v4-dialogue",
inputs: [
{ text: "[excited] Did you see the launch?", voice: "JBFqnCBsd6RMkjVDRZzb" },
{ text: "[laughs] I watched it twice!", voice: "EXAVITQu4vr4xnSDxMaL" },
],
}),
});
if (!response.ok) throw new Error(await response.text());
await writeFile("dialogue.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": "inputs: dialogue text must total at most 2000 characters",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request"
}
}
POST /v1/audio/dialogue turns an ordered list of lines into one audio file of a conversation. Each line names its own voice, so you can script an interview, a podcast intro, or a scene without stitching clips together. The response body is the audio file.
| Model | Price |
|---|---|
eleven-v4-dialogue | $0.12 per 1,000 characters |
eleven-v3-dialogue | $0.12 per 1,000 characters |
string
required
eleven-v4-dialogue or eleven-v3-dialogue.object[]
required
1 to 50 lines in speaking order. The text of all lines totals at most 2,000
characters, and one dialogue uses at most 10 different voices.
Show line
Show line
string
required
The line to speak. Blank text and control characters other than
newlines are rejected.
string
required
A voice ID or first name from the
voice list.
string
default:"mp3"
mp3, opus, pcm, or wav. See
Output formats.string
An ISO 639 language code of two or three lowercase letters, such as
en.
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.integer
From
0 to 4294967295. Makes repeated requests more repeatable, without
guaranteeing identical audio.invalid_request.
Direct the delivery
Lines can carry audio tags in square brackets, such as[excited] or
[laughs], to shape how they’re spoken. Tags are part of text, so they
count toward the 2,000-character total and are billed.
{
"model": "eleven-v3-dialogue",
"inputs": [
{ "text": "[excited] Did you see the launch?", "voice": "George" },
{ "text": "[laughs] I watched it twice!", "voice": "Sarah" }
]
}
Response
A successful request returns HTTP200 with the audio as the body. The
Content-Type matches response_format, for example audio/mpeg, and the
response carries x-gateway-execution-id.
Billing
The request is billed per character across every line’stext, counted in
Unicode code points. A full 2,000-character dialogue costs $0.24. The price is
known before the provider is called, and failed requests aren’t charged. This
endpoint rejects Idempotency-Key; if a request times out, check your Console
request logs before you retry.
Errors
| Status | error.code | Cause |
|---|---|---|
| 400 | invalid_request | A field is unknown or invalid, the lines total more than 2,000 characters, the dialogue uses more than 10 voices, or a voice isn’t in the list. |
| 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. |
| 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). |
| 503 | no_eligible_route | The model doesn’t run on this endpoint, or no route is available right now. |
curl https://api.snapgen.org/v1/audio/dialogue \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "eleven-v4-dialogue",
"inputs": [
{
"text": "[excited] Did you see the launch?",
"voice": "JBFqnCBsd6RMkjVDRZzb"
},
{
"text": "[laughs] I watched it twice!",
"voice": "EXAVITQu4vr4xnSDxMaL"
}
]
}' \
--output dialogue.mp3
import json
import os
from urllib.request import Request, urlopen
body = {
"model": "eleven-v4-dialogue",
"inputs": [
{"text": "[excited] Did you see the launch?", "voice": "JBFqnCBsd6RMkjVDRZzb"},
{"text": "[laughs] I watched it twice!", "voice": "EXAVITQu4vr4xnSDxMaL"},
],
}
request = Request(
"https://api.snapgen.org/v1/audio/dialogue",
data=json.dumps(body).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response, open("dialogue.mp3", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.snapgen.org/v1/audio/dialogue", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "eleven-v4-dialogue",
inputs: [
{ text: "[excited] Did you see the launch?", voice: "JBFqnCBsd6RMkjVDRZzb" },
{ text: "[laughs] I watched it twice!", voice: "EXAVITQu4vr4xnSDxMaL" },
],
}),
});
if (!response.ok) throw new Error(await response.text());
await writeFile("dialogue.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": "inputs: dialogue text must total at most 2000 characters",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request"
}
}