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

macOS / Linux (Homebrew)
brew install leanvox/tap/lvox
macOS / Linux (Shell)
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/leanvox/lvox/releases/latest/download/lvox-installer.sh | sh
Windows (PowerShell)
irm https://github.com/leanvox/lvox/releases/latest/download/lvox-installer.ps1 | iex

Login

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.mp3

Streaming

# Stream audio (writes chunks as they arrive)
lvox stream "Hello world!" -o hello.mp3

Other 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 --json

Full 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_KEY

Generate API keys from the dashboard. Keys use the lv_live_ prefix and are hashed server-side — save them when created.

Register

POST /v1/auth/register
{ "email": "[email protected]", "password": "your-password" }

Login

POST /v1/auth/login
{ "email": "[email protected]", "password": "your-password" }

Returns: { "token": "jwt...", "user": { "id", "email" } }

Generate Speech

POST /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

ModelBest ForPriceFeatures
standardEveryday TTS$0.005/1K (~1 min)Fast, 54+ voices, 10 languages
proEmotions & 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.

POST /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:

HeaderDescription
X-Leanvox-Request-IdUnique request identifier
X-Leanvox-Cost-CentsCredits charged (in cents)
X-Leanvox-Balance-CentsRemaining balance after charge
X-Leanvox-CharactersCharacter 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.mp3

Example (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.

POST /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

POST /v1/files/extract-text

Multipart 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/stream

CLI (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.mp3

Voices

List Voices

GET /v1/voices?model=standard

Curated Pro Voices

GET /v1/voices/curated

Returns 14 curated AI-designed Pro voices with preview audio URLs.

Clone Voice (Pro)

POST /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

POST /v1/voices/{voice_id}/unlock

Unlock a cloned voice for TTS use. Costs $3.00 per voice.

Design Voice

POST /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

DELETE /v1/voices/{voice_id}

Generations

Browse and manage your past TTS generations.

List Generations

GET /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

GET /v1/generations/{id}/audio
{ "audio_url": "https://cdn.leanvox.com/audio/abc123.mp3?token=..." }

Returns a presigned URL valid for 1 hour.

Delete Generation

DELETE /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

POST /v1/tts/generate/async

Same body as /v1/tts/generate. Optional "webhook_url" for completion callback.

Check Job Status

GET /v1/tts/jobs/{job_id}
{
  "id": "uuid",
  "status": "pending" | "processing" | "completed" | "failed",
  "audio_url": "...",      // when completed
  "error": "...",          // when failed
  "created_at": "..."
}

Account

Get Balance

GET /v1/account/balance
{ "balance_cents": 450, "total_spent_cents": 50 }

Usage History

GET /v1/account/usage?days=30&model=standard&limit=100

Buy Credits

POST /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" } }
HTTPCodeMeaning
400invalid_requestBad parameters
401invalid_api_keyMissing or invalid key
402insufficient_balanceNot enough credits
404not_foundResource not found
429rate_limit_exceeded10/min (free) or 60/min (paid)
500server_errorInternal error

Python SDK

Official Python client for the Leanvox API. Sync and async support, zero config.

Install

pip install leanvox

Quick Start

Sync
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")
Async
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 file

Error 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}")

PyPI · GitHub

Node.js SDK

Official Node.js/TypeScript client. Full type safety, zero runtime dependencies, Node.js 18+.

Install

npm install leanvox

Quick 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 file

Error 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}`);
  }
}

npm · GitHub

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

~/.claude/claude_desktop_config.json
{
  "mcpServers": {
    "leanvox": {
      "command": "npx",
      "args": ["-y", "@leanvox/mcp-server"],
      "env": {
        "LEANVOX_API_KEY": "lv_live_your_key_here"
      }
    }
  }
}

Cursor

Settings → MCP → Add Server
{
  "leanvox": {
    "command": "npx",
    "args": ["-y", "@leanvox/mcp-server"],
    "env": {
      "LEANVOX_API_KEY": "lv_live_your_key_here"
    }
  }
}

VS Code (Copilot)

.vscode/mcp.json
{
  "servers": {
    "leanvox": {
      "command": "npx",
      "args": ["-y", "@leanvox/mcp-server"],
      "env": {
        "LEANVOX_API_KEY": "lv_live_your_key_here"
      }
    }
  }
}

Available Tools

ToolDescription
leanvox_generateGenerate speech from text
leanvox_streamStream audio to a file
leanvox_dialogueCreate multi-speaker dialogue
leanvox_list_voicesBrowse available voices
leanvox_check_balanceCheck account balance

Resources

URIDescription
leanvox://voicesAll available voices
leanvox://voices/curated14 curated Pro voices
leanvox://generationsPast generations
leanvox://accountBalance & usage

Prompts

PromptDescription
narrateConvert text to natural speech
podcastCreate a multi-speaker podcast
voice-cloneClone a voice from reference audio

npm · GitHub

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

GitHub