Skip to content

API Reference

Base URL https://api.vocallab.ai. Every request needs Authorization: Bearer vl_live_... (see API Keys).

GET /api/v1/ping

Connection + auth test. Returns your points balance.

{ "ok": true, "points": 12345, "unit": "points (1 pt ≈ 1 second of audio)" }

GET /api/v1/voices

Voices you can use — your own cloned and designed voices first, then the public catalog. Use a returned id as the voice for generation. Catalog voices also accept their slug.

QueryDefaultNotes
limit(none)How many to return, 1–500. Omitted returns the whole list — more than 260 voices
offset0How many to skip, for paging
q—Case-insensitive filter on the voice id, slug and name, e.g. ?q=ashley
type—preset, clone or designed

Every response carries total, count, offset and has_more, so you can page without guessing. The same four parameters (minus type) work on GET /api/v1/voices/clones and GET /api/v1/voices/designs.

curl "https://api.vocallab.ai/api/v1/voices?q=narrator&limit=20" \
  -H "Authorization: Bearer vl_live_..."
{ "voices": [
  { "id": "Ashley", "slug": "warm-natural-female-explainer-voice-for-youtube-podcasts",
    "name": "Warm Natural Female Explainer Voice for YouTube & Podcasts",
    "type": "preset", "languages": ["en"], "language_code": "en-US",
    "accent": "American", "category": "Narration" },
  { "id": "default-abc123__my-voice", "name": "My Narrator", "type": "clone", "languages": ["en-GB"] }
] }

languages is the bare language and flattens every accent together. language_code and accent (presets only) say which English or Portuguese a voice actually is — use them to pick a British or European Portuguese voice without guessing from the name.

GET /api/v1/models

The selectable speech models. Pass a key as model on POST /api/v1/tts (default v-pro). API keys can use all of them. See Voice Models for the differences.

{ "default": "v-pro", "models": [
  { "key": "v-studio", "label": "VocalLab Studio",       "steerable": true,  "costMultiplier": 1,
    "supportsTemperature": false, "supportsDeliveryMode": true },
  { "key": "v-flash",  "label": "VocalLab Studio Flash", "steerable": false, "costMultiplier": 0.75,
    "supportsTemperature": false, "supportsDeliveryMode": false },
  { "key": "v-pro",    "label": "VocalLab Pro",          "steerable": false, "costMultiplier": 1,
    "supportsTemperature": true,  "supportsDeliveryMode": false },
  { "key": "v-lite",   "label": "VocalLab Lite",         "steerable": false, "costMultiplier": 0.5,
    "supportsTemperature": true,  "supportsDeliveryMode": false }
] }

supportsTemperature and supportsDeliveryMode tell you which expressiveness control a model actually honours — they do not overlap. Send the wrong one and it is rejected rather than silently ignored.

POST /api/v1/tts

Generate speech.

FieldRequiredNotes
textyesUp to 2,000 characters. Each call is synthesized as a single request — for longer scripts, split the text and make multiple calls
voiceyesA voice id from /api/v1/voices
modelnov-studio (newest, steerable, 200+ languages), v-flash (200+ languages, ~5× faster, ¾ points), v-pro (default — highest fidelity, 15 languages), or v-lite (fast, ½ points). See GET /api/v1/models
speednoNumber 0.5–1.5 (step 0.05). Defaults to the voice preset
temperaturenoNumber 0.7–1.5 (step 0.05). Higher = more expressive & variable. Ignored by v-studio and v-flash — use delivery_mode on v-studio. Defaults to the voice preset
delivery_modenoSTABLE, BALANCED or CREATIVE — how much the model varies its performance. v-studio only; sending it with any other model returns 422
enhance_generationnotrue denoises the generated audio. Works on every model. Default false — it can leave a voice sounding over-smoothed
formatnoOne of MP3 (default), WAV, FLAC, OGG_OPUS, LINEAR16, PCM, ALAW, MULAW
bit_ratenoInteger 32000–320000. MP3 & OGG_OPUS only — ignored for other formats
sample_ratenoOne of 8000, 16000, 22050, 24000, 32000, 44100, 48000 (Hz)
captionsnotrue (or "phrase" / "word") also returns the SRT subtitles inline as captions, so you don't need a second call. See GET /api/v1/tts/:id/captions
curl -X POST https://api.vocallab.ai/api/v1/tts \
  -H "Authorization: Bearer vl_live_..." \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from VocalLab","voice":"Ashley"}'

Returns the audio inline (base64) plus an id. The hosted URL appears once the upload finishes — poll GET /api/v1/tts/:id for it.

Each request is capped at 2,000 characters and maps to exactly one speech synthesis — there's no hidden splitting or fan-out, so cost and latency per call stay predictable. Requests are metered against your points balance and run through a fair-use queue, so heavy automation never blocks the app. To narrate a longer script, break it into ≤ 2,000-character pieces and send them as separate calls (respecting the rate limit of 60 requests/minute, or 120 on Max).

{ "id": "...", "status": "pending", "audio_base64": "data:audio/mp3;base64,...",
  "audio_url": null, "stream_url": "https://api.vocallab.ai/api/v1/tts/.../audio",
  "captions_url": "https://api.vocallab.ai/api/v1/tts/.../captions",
  "format": "MP3", "model": "v-pro", "points_used": 12 }

Model & cost. v-flash bills at ¾ (points = ⌈ characters ÷ 20 ⌉) and v-lite at half (÷ 30) the points of v-pro/v-studio (÷ 15). v-studio is the only model that follows expression/steering instructions — see Voice Models and Voice Steering.

Pauses

Insert a silence of an exact length anywhere in text with a self-closing <break /> tag:

curl -X POST https://api.vocallab.ai/api/v1/tts \
  -H "Authorization: Bearer vl_live_..." \
  -H "Content-Type: application/json" \
  -d '{"text":"Let me think <break time=\"1.5s\" /> Yes, I have thought about it.","voice":"Ashley"}'
  • Seconds or milliseconds — <break time="1.5s" /> and <break time="1500ms" /> are identical.
  • Up to 20 break tags per request, in every supported language.
  • Tag names are case-insensitive, but the tag must be well-formed and self-closing.
  • Break tags count toward the 2,000-character limit and are stripped from captions/SRT.
  • Softer pacing needs no tag: commas, periods, em dashes (—) and blank lines all shape delivery. See Voice Steering.

GET /api/v1/tts/:id

Generation status and the hosted audio URL once ready.

{ "id": "...", "status": "ready", "audio_url": "https://...",
  "stream_url": "https://api.vocallab.ai/api/v1/tts/.../audio",
  "captions_url": "https://api.vocallab.ai/api/v1/tts/.../captions", "format": "MP3" }

GET /api/v1/tts/:id/audio

Stream the finished clip's audio bytes directly — an alternative to the base64 audio_base64 blob. Served inline (plays in a browser or media player) and supports the HTTP Range header so clients can seek. Returns 409 while the clip is still finalizing — poll GET /api/v1/tts/:id until status is ready, then stream.

curl -L https://api.vocallab.ai/api/v1/tts/GENERATION_ID/audio \
  -H "Authorization: Bearer vl_live_..." \
  -o speech.mp3

The same link is returned as stream_url on POST /api/v1/tts and GET /api/v1/tts/:id.

GET /api/v1/tts/:id/captions

Subtitles for a generation, as an .srt file. Cues are timed from the actual speech (word-level timings returned with the synthesis), and any [emotion] markup or <break /> pause tag is stripped out, so only spoken words reach the file.

Captions read from stored timings, not from the audio file — they're available immediately, even while status is still pending.

QueryDefaultNotes
formatsrtsrt returns the subtitle file; json returns { srt, words, duration } for custom rendering
modephrasephrase = readable subtitle lines (split on pauses, sentence and clause ends). word = one cue per word, for karaoke-style highlighting
max_words4Words per line in phrase mode, 2–7
uppercasefalsetrue UPPERCASES the caption text
curl -L "https://api.vocallab.ai/api/v1/tts/GENERATION_ID/captions" \
  -H "Authorization: Bearer vl_live_..." \
  -o speech.srt
1
00:00:00,000 --> 00:00:01,240
Hello from VocalLab,

2
00:00:01,240 --> 00:00:03,100
this is your narrator speaking.

With format=json:

{ "id": "...", "mode": "phrase", "duration": 3.1,
  "srt": "1\n00:00:00,000 --> 00:00:01,240\nHello from VocalLab,\n\n...",
  "words": [ { "word": "Hello", "start": 0, "end": 0.32 } ] }

The same link is returned as captions_url on POST /api/v1/tts and GET /api/v1/tts/:id. To get the SRT back with the audio in a single call, send "captions": true on POST /api/v1/tts.

Returns 409 captions_unavailable when a generation has no stored word timings (a few older clips) — the audio is unaffected.

DELETE /api/v1/tts/:id

Permanently delete a generation you own — both the stored audio file and its database record. Handy for cleaning up after a download or honoring a data-deletion request. An id that doesn't exist, or isn't yours, returns 404.

curl -X DELETE https://api.vocallab.ai/api/v1/tts/GENERATION_ID \
  -H "Authorization: Bearer vl_live_..."
{ "ok": true, "id": "...", "deleted": true }

GET /api/v1/voices/languages

The languages and recording limits that apply to voice cloning and voice design. Read this instead of hardcoding the list — it's the same source of truth the Studio upload form uses.

Over 470 languages are returned, each with any accent variants it offers. code is a BCP-47 tag; pass either the language's own code or one of its accent codes as language when you clone or design.

experimental: true means the language has no stock voice catalog behind it — cloning and design still work, but quality varies more.

{ "languages": [
    { "name": "Auto-detect", "code": "auto", "experimental": true, "accents": [] },
    { "name": "English", "code": "en", "experimental": false, "accents": [
        { "name": "American", "code": "en-US" },
        { "name": "British",  "code": "en-GB" },
        { "name": "Indian",   "code": "en-IN" }
      ] },
    { "name": "Portuguese", "code": "pt", "experimental": false, "accents": [
        { "name": "Brazilian", "code": "pt-BR" },
        { "name": "European",  "code": "pt-PT" }
      ] }
  ],
  "sample_limits": { "max_samples": 1, "max_base64_length": 4194304,
                     "allowed_formats": ["mp3", "wav", "webm"] } }

Changed: code used to be an enum of 16 values (EN_US, PT_BR, …) with an Other entry meaning "let the engine guess". Those old values are still accepted wherever a language is taken, so existing integrations keep working — but the list they came from is now the full set, and Other is the explicit auto entry.

Voice design

Invent a brand-new voice from a written description — no recording needed. It's two calls: generate candidate previews, then save the one you like.

POST /api/v1/voices/designs/previews

Generates up to three candidate voices. Billed in points — each preview is a real synthesis of preview_text at the API rate (⌈ characters ÷ 15 ⌉ per preview).

Nothing is saved yet, so this doesn't use a design slot — but it does require a free one. With your designs already at the plan limit the call returns 409 design_limit and no points are spent, because a voice you couldn't save isn't worth paying for.

FieldRequiredNotes
promptyesDescribe the voice in English, 30–250 characters: gender, age, accent, tone, pace and delivery
languagenoLanguage the voice should speak (English, ES_ES, …). Defaults to auto — inferred from the prompt
preview_textnoWhat the previews say, up to 1,000 characters. Defaults to a short sample line
countnoHow many candidates to generate, 1–3 (default 3). Each one is billed
include_audionotrue also returns each preview as base64. Every preview already has a preview_url, so the bytes are opt-in
curl -X POST https://api.vocallab.ai/api/v1/voices/designs/previews \
  -H "Authorization: Bearer vl_live_..." \
  -H "Content-Type: application/json" \
  -d '{"prompt":"A warm, unhurried British woman in her forties, low and reassuring, like a documentary narrator.","count":2}'
{ "previews": [
    { "preview_id": "...", "text": "Hello! This is a preview of my voice...",
      "preview_url": "https://api.vocallab.ai/api/v1/audio/..." }
  ],
  "points_used": 12, "expires_in": 1800 }

A preview_id is not a usable voice yet, and it's valid for about 30 minutes.

Previews you don't keep are thrown away. Save the one you want before generating another set — calling this endpoint again discards your previous previews and their preview_urls stop working, and saving one discards the rest of its batch. Nothing is left behind either way.

POST /api/v1/voices/designs

Keeps a preview as a permanent voice. Costs no points (the previews were already billed) but uses one of your plan's design slots. The returned id works immediately as the voice on POST /api/v1/tts.

FieldRequiredNotes
preview_idyesA preview_id from the previews call, within 30 minutes
nameyesDisplay name, up to 80 characters
descriptionnoFree-text note, up to 500 characters
tagsnoUp to 10 labels
sample_base64noAudition clip to store with the voice. Not needed inside the 30-minute window — the preview you chose is kept automatically
{ "id": "default-abc123__narrator", "name": "Documentary Narrator", "type": "designed",
  "languages": ["en-GB"], "created_at": "...",
  "preview_url": "https://api.vocallab.ai/api/v1/audio/...", "used": 3, "limit": 20 }

GET /api/v1/voices/designs

Your designed voices, newest first, with slot usage: { "voices": [...], "used": 3, "limit": 20 }.

DELETE /api/v1/voices/designs/:voiceId

Deletes the voice from the provider and from your library. On a paid plan this frees a slot. Returns { "ok": true, "id": "...", "deleted": true }.

Voice cloning

Recreate a specific voice from one short recording. Cloning costs no points — it's limited by the number of clone slots on your plan. Only clone a voice you own or have written permission to use.

POST /api/v1/voices/clones

FieldRequiredNotes
nameyesDisplay name, up to 80 characters
languageyesA name or code from GET /api/v1/voices/languages — a language (en), an accent (en-GB), or a legacy enum (EN_US). Use auto to have it detected
samplesyes[{ "audio_base64": "...", "transcript": "..." }] — one recording of about 30 seconds. transcript is optional but noticeably improves the clone
remove_background_noisenoClean the recording first. Default true

Almost any audio file works. audio_base64 is the raw base64 of your recording, with no data: prefix — MP3, WAV, M4A, AAC, OGG, FLAC, WebM, the audio track of an MP4, and more. Anything the speech provider wouldn't accept directly is converted server-side to mono 16-bit WAV, exactly as the web apps do in the browser before uploading, so the API is no fussier than the Studio.

The conversion also:

  • trims to the first 30 seconds — a longer file is fine to send, only the opening is used;
  • down-mixes stereo to mono and resamples to 32 kHz;
  • shrinks oversized files, so a big 24-bit studio WAV no longer has to be prepared by hand.

The limit is on the base64 string, not the decoded bytes: up to 8,000,000 characters, roughly a 6 MB file. Anything larger returns 413 sample_too_large; a file with no readable audio returns 400 unreadable_audio.

When anything was changed, the response includes an audio_notes array saying so — worth logging, so a clone built from the first 30 seconds of a long recording is never a silent surprise:

{ "id": "...", "name": "My Narrator", "type": "clone", "used": 2, "limit": 100,
  "audio_notes": ["Re-encoded to mono 16-bit WAV at 32 kHz — aac isn't a format the provider accepts.",
                  "Trimmed to the first 30s (the sample was 90s)."] }
curl -X POST https://api.vocallab.ai/api/v1/voices/clones \
  -H "Authorization: Bearer vl_live_..." \
  -H "Content-Type: application/json" \
  -d "{\"name\":\"My Narrator\",\"language\":\"English\",\"samples\":[{\"audio_base64\":\"$(base64 -w0 sample.mp3)\"}]}"
{ "id": "default-abc123__my-narrator", "name": "My Narrator", "type": "clone",
  "languages": ["en-GB"], "created_at": "...", "used": 2, "limit": 100 }

GET /api/v1/voices/clones

Your cloned voices, newest first, with slot usage: { "voices": [...], "used": 2, "limit": 100 }.

DELETE /api/v1/voices/clones/:voiceId

Deletes the voice from the provider and from your library. On a paid plan this frees a slot. Returns { "ok": true, "id": "...", "deleted": true }.

Slots are separate. Cloned and designed voices never share a budget — see Plans & Limits for the per-plan numbers. On the Free plan the cap is a lifetime one: deleting a voice does not free the slot. In a workspace, members work within the owner's plan limits, and design points come out of the owner's balance.

What costs points

Points
Cloning a voicenone — limited by clone slots only
Listing or deleting a voicenone
Designing a voice (previews)count × ⌈ preview_text ÷ 15 ⌉ — every preview is a real synthesis
Saving a designed voicenone — the previews were already billed

GET /api/v1/me

Your points balance and plan.

Errors

StatusMeaning
400A required field is missing from the body (missing_text, missing_voice, missing_name, missing_prompt, …)
401Missing or invalid API key
402Not enough points
403Plan doesn't include API access (requires Pro or higher)
404Generation or voice not found (or not yours)
409Audio not ready yet — poll until ready; no word timings stored (captions); or every clone/design slot is in use (clone_limit, design_limit)
413Text, preview text, or audio sample too long
422Unknown model, unsupported language, or an out-of-range parameter
429Rate limit reached — 60 requests / minute per key (120 on Max), and separately 60 cloning or voice-design calls per hour
502The speech provider failed or rejected the request — the message says why, and retryable says whether to try again

Errors are shaped { "error": { "code": "...", "message": "..." } }.

Pricing

Generations are billed from your points balance, calculated from the text length (not the final audio duration):

points = ⌈ characters ÷ 15 ⌉      (API rate — about 15 characters per point)

⌈ ⌉ rounds up to the next whole point. The web app uses 17 characters per point, so the API carries a small (~13%) premium. Points are metered before the upstream call — a request that would exceed your balance returns 402 and is never generated. Each response includes the exact points_used. Because speech averages ~15–17 characters/second, 1 point ≈ 1 second of audio, but the exact charge always follows the formula above. See Credits & Minutes for more.

Didn't find what you were looking for?

Talk to Us

Try it yourself

Most answers here take a minute to try in Studio.

Open Studio