API Quick Start
Create a key, pick a voice and generate your first audio.
Create an API key
Sign in, open API Keys, click "New key" and copy the key that starts with sk-. It is shown only once.
Save it as an environment variable - the examples below use it:
export VAL_API_KEY="sk-your-api-key"Check your account
curl https://voiceailabs.com/api/v1/account \
-H "Authorization: Bearer $VAL_API_KEY"credits.balance is your available credits, and limits.max_text_length is the maximum characters per synthesis.
Pick a voice
Use a public voice from the Voice Square (it doesn't use a voice slot) or one of your cloned voices:
# Public voices
curl "https://voiceailabs.com/api/v1/voices/public?limit=5" \
-H "Authorization: Bearer $VAL_API_KEY"
# Your voices
curl "https://voiceailabs.com/api/v1/voices" \
-H "Authorization: Bearer $VAL_API_KEY"Note a voice's id - you'll pass it as voice_id.
Create a task
curl https://voiceailabs.com/api/v1/tts \
-H "Authorization: Bearer $VAL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1024-intro" \
-d '{
"voice_id": "VOICE_ID",
"text": "Welcome to Voice AI Labs.",
"language": "en"
}'You get 202 and a task with status: "pending". The Idempotency-Key makes network retries safe - no duplicate tasks or charges. See Errors & Limits.
Get the audio
Poll every few seconds until status is completed or failed:
curl https://voiceailabs.com/api/v1/syntheses/SYNTHESIS_ID \
-H "Authorization: Bearer $VAL_API_KEY"Download the MP3 from audio_url. In production, use a webhook instead of polling.
Complete example
const BASE_URL = "https://voiceailabs.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.VAL_API_KEY}` };
async function call(path, init = {}) {
const response = await fetch(`${BASE_URL}${path}`, {
...init,
headers: { ...headers, ...init.headers },
});
const body = await response.json();
if (body.code !== 0) {
const error = new Error(body.message);
Object.assign(error, body.data); // error_code, retryable, request_id
throw error;
}
return body.data;
}
async function textToSpeech(voiceId, text) {
let task = await call("/tts", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({ voice_id: voiceId, text }),
});
while (task.status === "pending" || task.status === "processing") {
await new Promise((resolve) => setTimeout(resolve, 3000));
task = await call(`/syntheses/${task.id}`);
}
if (task.status === "failed") {
throw new Error(`Synthesis failed, refund: ${task.refund?.status}`);
}
return task.audio_url;
}
textToSpeech("VOICE_ID", "Hello from Voice AI Labs!").then(console.log);import os
import time
import uuid
import requests
BASE_URL = "https://voiceailabs.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['VAL_API_KEY']}"}
def call(method, path, **kwargs):
headers = {**HEADERS, **kwargs.pop("headers", {})}
body = requests.request(method, f"{BASE_URL}{path}", headers=headers, timeout=60, **kwargs).json()
if body["code"] != 0:
raise RuntimeError(f"{body['data']['error_code']}: {body['message']} ({body['data']['request_id']})")
return body["data"]
def text_to_speech(voice_id, text):
task = call(
"POST",
"/tts",
json={"voice_id": voice_id, "text": text},
headers={"Idempotency-Key": str(uuid.uuid4())},
)
while task["status"] in ("pending", "processing"):
time.sleep(3)
task = call("GET", f"/syntheses/{task['id']}")
if task["status"] == "failed":
raise RuntimeError(f"Synthesis failed, refund: {task['refund']}")
return task["audio_url"]
print(text_to_speech("VOICE_ID", "Hello from Voice AI Labs!"))Next steps
- Submit many texts at once: batch text-to-speech
- Use your own voice: clone a voice
- Stop polling: webhooks