API Reference
Base URL: https://api.leanvox.com
Try endpoints interactively in the OpenAPI playground.
CLI
Generate speech directly from your terminal with lvox.
Install
brew install leanvox/tap/lvoxcurl --proto '=https' --tlsv1.2 -LsSf https://github.com/leanvox/lvox/releases/latest/download/lvox-installer.sh | shirm https://github.com/leanvox/lvox/releases/latest/download/lvox-installer.ps1 | iexLogin
lvox auth login YOUR_API_KEY Saves your API key to ~/.lvox/config.toml. Or set LEANVOX_API_KEY env var.
Generate Speech
# Basic usage
lvox generate "Hello world!" -o hello.mp3
# With voice and model
lvox gen "Welcome to Leanvox!" --voice af_heart --model standard -o welcome.mp3
# Pro model with emotion
lvox gen "This is amazing! [laugh]" --model pro --voice emma -o emotion.mp3
# From text file
lvox gen --file script.txt -o narration.mp3
# From EPUB (book → audiobook!)
lvox gen --file my-book.epub --voice af_heart -o audiobook.mp3
# Pipe from stdin
echo "Hello from stdin" | lvox gen -o piped.mp3Streaming
# Stream audio (writes chunks as they arrive)
lvox stream "Hello world!" -o hello.mp3Other Commands
# List voices
lvox voices list
lvox voices list --model pro
# Check balance
lvox balance
# View usage history
lvox usage --days 7
# List past generations
lvox history
lvox history audio <id> -o replay.mp3
# JSON output (all commands)
lvox balance --jsonFull CLI reference: lvox --help or lvox <command> --help
Authentication
All API requests require an API key passed via the Authorization header.
Authorization: Bearer lv_live_YOUR_API_KEYGenerate API keys from the dashboard. Keys use the lv_live_ prefix and are hashed server-side — save them when created.
Register
/v1/auth/register{ "email": "[email protected]", "password": "your-password" }Login
/v1/auth/login{ "email": "[email protected]", "password": "your-password" } Returns: { "token": "jwt...", "user": { "id", "email" } }
Generate Speech
/v1/tts/generate{
"text": "Hello world!",
"model": "standard", // "standard" or "pro"
"voice": "af_heart", // voice ID
"language": "en", // ISO 639-1 code
"format": "mp3", // optional: "mp3" (default) or "wav"
"speed": 1.0, // optional: 0.5 - 2.0
"exaggeration": 0.5 // optional (pro only): 0.0 - 1.0
}Response
{
"audio_url": "https://cdn.leanvox.com/audio/abc123.mp3",
"model": "standard",
"voice": "af_heart",
"characters": 12,
"cost_cents": 0.06
}Models
| Model | Best For | Price | Features |
|---|---|---|---|
| standard | Everyday TTS | $0.005/1K (~1 min) | Fast, 54+ voices, 10 languages |
| pro | Emotions & expressions | $0.01/1K (~1 min) | Voice cloning, emotion tags, 23 languages |
Note: Minimum 1,000 characters (~1 minute of audio) billed per request. Requests under 1,000 characters are rounded up.
Streaming
Stream audio as it's generated instead of waiting for the full response. Supports both Standard and Pro models.
/v1/tts/stream{
"text": "Hello world!",
"model": "standard", // "standard" or "pro"
"voice": "af_heart", // voice ID
"language": "en", // ISO 639-1 code
"exaggeration": 0.5 // optional (pro only): 0.0 - 1.0
} Same request body as /v1/tts/generate. Always returns MP3 format.
Response
Returns a raw audio/mpeg byte stream. Cost and billing metadata are in the response headers:
| Header | Description |
|---|---|
X-Leanvox-Request-Id | Unique request identifier |
X-Leanvox-Cost-Cents | Credits charged (in cents) |
X-Leanvox-Balance-Cents | Remaining balance after charge |
X-Leanvox-Characters | Character count of input text |
Example (curl)
curl -N -X POST https://api.leanvox.com/v1/tts/stream \
-H "Authorization: Bearer lv_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Hello world!", "model": "standard", "voice": "af_heart"}' \
--output speech.mp3Example (JavaScript)
const res = await fetch("https://api.leanvox.com/v1/tts/stream", {
method: "POST",
headers: {
"Authorization": "Bearer lv_live_YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ text: "Hello world!", model: "standard" }),
});
// Pipe to audio element via MediaSource API or save as blob
const blob = await res.blob();
const audioUrl = URL.createObjectURL(blob);Dialogue
Generate multi-speaker dialogue in a single request. Supports both Standard and Pro models.
/v1/tts/dialogue{
"model": "pro",
"lines": [
{ "text": "Hi there!", "voice": "emma", "language": "en" },
{ "text": "Hello! [laugh]", "voice": "james", "language": "en", "exaggeration": 0.7 }
],
"gap_ms": 500 // silence between lines (default 500)
}File Upload
Extract plain text from .txt or .epub files so you can pipe the result straight into TTS generation.
Useful for narrating long documents, ebooks, or anything you'd rather not paste by hand.
The CLI handles this automatically — pass any .epub or .txt file via --file and it extracts the text locally before sending.
The API endpoint below is for web clients or custom integrations that need server-side extraction.
Extract Text from File
/v1/files/extract-textMultipart form upload. Auth required. Max 5 MB. Returns up to 500,000 characters.
// Request: multipart/form-data with field "file"
// Accepted types: .txt, .epub
// Response
{
"text": "Chapter 1. It was a dark and stormy night...",
"filename": "my-book.epub",
"char_count": 142830,
"truncated": false // true if file exceeded 500K chars
}Examples
cURL
curl -X POST https://api.leanvox.com/v1/files/extract-text \
-H "Authorization: Bearer lv_live_..." \
-F "[email protected]"JavaScript (browser)
const form = new FormData();
form.append('file', fileInput.files[0]);
const res = await fetch('https://api.leanvox.com/v1/files/extract-text', {
method: 'POST',
headers: { Authorization: 'Bearer lv_live_...' },
body: form,
});
const { text } = await res.json();
// Now pass text to /v1/tts/generate or /v1/tts/streamCLI (handles extraction automatically)
# .txt file
lvox gen --file chapter.txt --voice af_heart -o chapter.mp3
# .epub file — parsed locally, spine-ordered, HTML stripped
lvox gen --file my-book.epub --voice af_heart -o audiobook.mp3Voices
List Voices
/v1/voices?model=standardCurated Pro Voices
/v1/voices/curatedReturns 14 curated AI-designed Pro voices with preview audio URLs.
Clone Voice (Pro)
/v1/voices/clone{
"name": "My Voice",
"audio_base64": "<base64-encoded WAV>",
"description": "optional description"
} Upload a 5-30 second WAV clip. Returns a voice_id for use with the Pro model.
Unlock Voice
/v1/voices/{voice_id}/unlockUnlock a cloned voice for TTS use. Costs $3.00 per voice.
Design Voice
/v1/voices/design{
"name": "Deep Narrator",
"description": "A deep, warm male voice for audiobooks",
"language": "en",
"notes": "optional extra guidance"
} AI-generates a custom voice from a text description. Costs $1.00 per voice.
Delete Voice
/v1/voices/{voice_id}Generations
Browse and manage your past TTS generations.
List Generations
/v1/generations?limit=20&offset=0{
"generations": [
{
"id": "uuid",
"generation_type": "tts",
"input_text": "Hello world!",
"model": "standard",
"voice": "af_heart",
"format": "mp3",
"characters": 12,
"cost_cents": 1,
"created_at": "2025-01-15T10:30:00Z"
}
],
"total": 42,
"limit": 20,
"offset": 0
}Get Generation Audio
/v1/generations/{id}/audio{ "audio_url": "https://cdn.leanvox.com/audio/abc123.mp3?token=..." } Returns a presigned URL valid for 1 hour.
Delete Generation
/v1/generations/{id}Permanently deletes the generation and its audio file from storage.
Async Jobs
For long texts, use async generation. You get a job ID immediately and poll for completion.
Create Async Job
/v1/tts/generate/asyncSame body as /v1/tts/generate. Optional "webhook_url" for completion callback.
Check Job Status
/v1/tts/jobs/{job_id}{
"id": "uuid",
"status": "pending" | "processing" | "completed" | "failed",
"audio_url": "...", // when completed
"error": "...", // when failed
"created_at": "..."
}Account
Get Balance
/v1/account/balance{ "balance_cents": 450, "total_spent_cents": 50 }Usage History
/v1/account/usage?days=30&model=standard&limit=100Buy Credits
/v1/account/credits{ "amount_cents": 2000 } Returns a Stripe checkout URL. Tiers: $5 (0%), $20 (+10%), $50 (+15%), $100 (+20% bonus).
Errors
All errors follow a consistent format:
{ "error": { "code": "error_code", "message": "Human-readable message" } }| HTTP | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Bad parameters |
| 401 | invalid_api_key | Missing or invalid key |
| 402 | insufficient_balance | Not enough credits |
| 404 | not_found | Resource not found |
| 429 | rate_limit_exceeded | 10/min (free) or 60/min (paid) |
| 500 | server_error | Internal error |
Python SDK
Official Python client for the Leanvox API. Sync and async support, zero config.
Install
pip install leanvoxQuick Start
from leanvox import Leanvox
client = Leanvox() # uses LEANVOX_API_KEY env var
result = client.generate(text="Hello from Leanvox!")
print(result.audio_url)
# Save directly to file
result.save("hello.mp3")from leanvox import AsyncLeanvox
async with AsyncLeanvox() as client:
result = await client.generate(text="Hello async world!")
print(result.audio_url)Generate Speech
result = client.generate(
text="Welcome to the future of voice.",
model="pro", # "standard" or "pro"
voice="emma",
language="en",
speed=1.0,
exaggeration=0.5, # pro only: 0.0 - 1.0
)
result.save("welcome.mp3")Stream Audio
with client.stream(text="A long narration about the universe...") as stream:
with open("narration.mp3", "wb") as f:
for chunk in stream:
f.write(chunk) Streaming always outputs MP3.
Dialogue
result = client.dialogue(
model="pro",
lines=[
{"text": "Welcome to the podcast!", "voice": "emma"},
{"text": "Thanks for having me.", "voice": "james"},
{"text": "Let's dive right in.", "voice": "emma"},
],
gap_ms=400,
)
result.save("podcast.mp3")Authentication
# Priority: constructor → env var → config file
client = Leanvox(api_key="lv_live_...") # 1. Explicit
# export LEANVOX_API_KEY="lv_live_..." # 2. Env var
# ~/.lvox/config.toml # 3. Config fileError Handling
from leanvox import (
Leanvox,
InsufficientBalanceError,
RateLimitError,
LeanvoxError,
)
try:
result = client.generate(text="Hello!")
except InsufficientBalanceError as e:
print(f"Low balance: ${e.balance_cents / 100:.2f}")
except RateLimitError as e:
print(f"Retry after {e.retry_after}s")
except LeanvoxError as e:
print(f"API error [{e.code}]: {e.message}")Node.js SDK
Official Node.js/TypeScript client. Full type safety, zero runtime dependencies, Node.js 18+.
Install
npm install leanvoxQuick Start
import { Leanvox } from "leanvox";
const client = new Leanvox(); // uses LEANVOX_API_KEY env var
const result = await client.generate({ text: "Hello from Leanvox!" });
console.log(result.audioUrl);
// Save directly to file
await result.save("hello.mp3");Generate Speech
const result = await client.generate({
text: "Welcome to the future of voice.",
model: "pro", // "standard" or "pro"
voice: "emma",
language: "en",
speed: 1.0,
exaggeration: 0.5, // pro only: 0.0 - 1.0
});
await result.save("welcome.mp3");Stream Audio
import { createWriteStream } from "fs";
const stream = await client.stream({
text: "A long narration about the universe...",
voice: "af_heart",
});
const writer = createWriteStream("narration.mp3");
for await (const chunk of stream) {
writer.write(chunk);
}
writer.end(); Streaming always outputs MP3.
Dialogue
const result = await client.dialogue({
model: "pro",
lines: [
{ text: "Welcome to the podcast!", voice: "emma" },
{ text: "Thanks for having me.", voice: "james" },
{ text: "Let's dive right in.", voice: "emma" },
],
gapMs: 400,
});
await result.save("podcast.mp3");Authentication
// Priority: constructor → env var → config file
const client = new Leanvox({ apiKey: "lv_live_..." }); // 1. Explicit
// export LEANVOX_API_KEY="lv_live_..." // 2. Env var
// ~/.lvox/config.toml // 3. Config fileError Handling
import {
Leanvox,
InsufficientBalanceError,
RateLimitError,
LeanvoxError,
} from "leanvox";
try {
const result = await client.generate({ text: "Hello!" });
} catch (e) {
if (e instanceof InsufficientBalanceError) {
console.log(`Low balance: $${(e.balanceCents / 100).toFixed(2)}`);
} else if (e instanceof RateLimitError) {
console.log(`Retry after ${e.retryAfter}s`);
} else if (e instanceof LeanvoxError) {
console.log(`API error [${e.code}]: ${e.message}`);
}
}MCP Server
Use Leanvox directly from Claude, ChatGPT, Cursor, VS Code, and any MCP-compatible AI assistant. Zero code required.
Install
npx @leanvox/mcp-server Or install globally: npm install -g @leanvox/mcp-server
Claude Desktop
{
"mcpServers": {
"leanvox": {
"command": "npx",
"args": ["-y", "@leanvox/mcp-server"],
"env": {
"LEANVOX_API_KEY": "lv_live_your_key_here"
}
}
}
}Cursor
{
"leanvox": {
"command": "npx",
"args": ["-y", "@leanvox/mcp-server"],
"env": {
"LEANVOX_API_KEY": "lv_live_your_key_here"
}
}
}VS Code (Copilot)
{
"servers": {
"leanvox": {
"command": "npx",
"args": ["-y", "@leanvox/mcp-server"],
"env": {
"LEANVOX_API_KEY": "lv_live_your_key_here"
}
}
}
}Available Tools
| Tool | Description |
|---|---|
| leanvox_generate | Generate speech from text |
| leanvox_stream | Stream audio to a file |
| leanvox_dialogue | Create multi-speaker dialogue |
| leanvox_list_voices | Browse available voices |
| leanvox_check_balance | Check account balance |
Resources
| URI | Description |
|---|---|
leanvox://voices | All available voices |
leanvox://voices/curated | 14 curated Pro voices |
leanvox://generations | Past generations |
leanvox://account | Balance & usage |
Prompts
| Prompt | Description |
|---|---|
| narrate | Convert text to natural speech |
| podcast | Create a multi-speaker podcast |
| voice-clone | Clone a voice from reference audio |
Tool Definitions
JSON schemas for LLM function calling. Drop these into any agent framework — OpenAI, Anthropic, Google, or custom — and let your LLM call Leanvox directly.
OpenAI Format
{
"type": "function",
"function": {
"name": "leanvox_generate",
"description": "Generate speech audio from text using Leanvox TTS API. Returns a URL to the generated audio file.",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "Text to convert to speech (max 10,000 chars)"
},
"model": {
"type": "string",
"enum": ["standard", "pro"],
"description": "TTS model. 'standard' for fast/cheap, 'pro' for highest quality with emotion control"
},
"voice": {
"type": "string",
"description": "Voice ID (e.g. 'af_heart', 'emma', 'james'). Use leanvox_list_voices to see options."
},
"language": {
"type": "string",
"description": "ISO 639-1 language code (default: 'en')"
},
"speed": {
"type": "number",
"description": "Playback speed 0.5-2.0 (default: 1.0)"
}
},
"required": ["text"],
"additionalProperties": false
}
}
}Anthropic Format
{
"name": "leanvox_generate",
"description": "Generate speech audio from text using Leanvox TTS API. Returns a URL to the generated audio file.",
"input_schema": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "Text to convert to speech (max 10,000 chars)"
},
"model": {
"type": "string",
"enum": ["standard", "pro"],
"description": "TTS model. 'standard' for fast/cheap, 'pro' for highest quality with emotion control"
},
"voice": {
"type": "string",
"description": "Voice ID (e.g. 'af_heart', 'emma', 'james'). Use leanvox_list_voices to see options."
},
"language": {
"type": "string",
"description": "ISO 639-1 language code (default: 'en')"
},
"speed": {
"type": "number",
"description": "Playback speed 0.5-2.0 (default: 1.0)"
}
},
"required": ["text"]
}
}Full tool definition files for leanvox_generate, leanvox_stream, leanvox_dialogue, leanvox_list_voices, and leanvox_check_balance are available on GitHub. Also served at:
https://api.leanvox.com/.well-known/ai-tools.json