Skip to content
Console

Get started

Quickstart

Copy page

Create an API key, turn text into speech, then queue an image and poll it until it is ready.

Three steps: a key, a request that answers with audio, and a request that is queued and polled.

Sign in to the console at https://app.lumavox.ai and open API keys. Your organisation’s master key is there, and you can create keys limited to the capabilities they need. A scoped key is what you want in production — see authentication.

Keep the key on your server and out of source control. The examples read it from an environment variable:

Shell
export API_KEY="paste-your-key-here"

POST /v1/tts answers with the audio itself — a WAV file unless you ask for something else.

curl
curl https://api.lumavox.ai/v1/tts \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from the API."}' \
--output hello.wav
JavaScript
// Node 18 or later: fetch is built in.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.lumavox.ai/v1/tts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text: "Hello from the API." }),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${response.status} ${error.code}: ${error.message}`);
}
await writeFile("hello.wav", Buffer.from(await response.arrayBuffer()));
console.log("saved hello.wav, request", response.headers.get("X-Request-Id"));
Python
import os
import requests
response = requests.post(
"https://api.lumavox.ai/v1/tts",
headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
json={"text": "Hello from the API."},
)
response.raise_for_status()
with open("hello.wav", "wb") as f:
f.write(response.content)
print("saved hello.wav, request", response.headers["X-Request-Id"])

Play hello.wav and you have your first generation.

With no voice_id you get the built-in voice. GET /v1/voices lists every voice your key may use; pass one’s id as voice_id:

curl
curl https://api.lumavox.ai/v1/voices -H "Authorization: Bearer $API_KEY"

Every response carries an X-Request-Id header. Keep it: it is what to quote when something goes wrong. Speech responses also say how long the audio is (X-Audio-Seconds), which model spoke (X-Model) and the rate it was produced at (X-Sample-Rate). Text to speech covers models, formats and streaming.

Images, video, stored audio, skyboxes and long transcriptions take longer than a request should wait. They answer 202 Accepted at once with a gen_… id, and you poll that id until it finishes.

curl
curl https://api.lumavox.ai/v1/images \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "A weathered brass diving helmet on wet slate, low winter light"}'

The response describes the job — what you asked for, what it cost, and where it stands:

Response — 202 (abridged)
{
"id": "gen_a1B2c3D4e5F6g7H8",
"status": "queued",
"capability": "image",
"model": "flux-2-max",
"prompt": "A weathered brass diving helmet on wet slate, low winter light",
"aspect_ratio": "1:1",
"width": 992,
"height": 992,
"megapixels": 1,
"output_format": "jpeg",
"credits_micros": 4000000,
"image": null,
"error": null,
"created_at": "2026-10-24T09:30:00.000Z",
"finished_at": null
}

Every response writes values the same way: times in UTC with milliseconds, credits as integers of micro-credits (4000000 is four credits), and null for a field with nothing in it. Conventions has the rules.

Poll it every couple of seconds:

curl
curl https://api.lumavox.ai/v1/images/gen_a1B2c3D4e5F6g7H8 \
-H "Authorization: Bearer $API_KEY"

queued, submitted and running mean keep polling. succeeded, failed and moderated are final. Once it has succeeded, the image is on the response:

Response — 200 (abridged)
{
"id": "gen_a1B2c3D4e5F6g7H8",
"status": "succeeded",
"capability": "image",
"image": {
"asset_id": "ast_x9Y8z7W6v5U4t3S2",
"url": "https://…",
"width": 992,
"height": 992,
"content_type": "image/jpeg",
"size_bytes": 284113
},
"error": null,
"created_at": "2026-10-24T09:30:00.000Z",
"finished_at": "2026-10-24T09:30:07.412Z"
}

image.url is a short-lived link — download the file promptly. The image is also kept in your library under image.asset_id, which is where to fetch it from later.

The same loop in code:

JavaScript
const headers = {
Authorization: `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
};
const submitted = await fetch("https://api.lumavox.ai/v1/images", {
method: "POST",
headers,
body: JSON.stringify({ prompt: "A lighthouse in fog, oil painting" }),
}).then((r) => r.json());
let job = submitted;
while (["queued", "submitted", "running"].includes(job.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
job = await fetch(`https://api.lumavox.ai/v1/images/${submitted.id}`, { headers }).then(
(r) => r.json(),
);
}
console.log(job.status, job.image?.url ?? job.error);
Python
import os
import time
import requests
headers = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
job = requests.post(
"https://api.lumavox.ai/v1/images",
headers=headers,
json={"prompt": "A lighthouse in fog, oil painting"},
).json()
while job["status"] in ("queued", "submitted", "running"):
time.sleep(2)
job = requests.get(f"https://api.lumavox.ai/v1/images/{job['id']}", headers=headers).json()
print(job["status"], job["image"]["url"] if job["image"] else job["error"])

A queued job is charged when it is accepted and refunded in full if it fails. Queued jobs has the details.

  • Authentication — scoped keys, and ephemeral keys for your users’ devices.
  • Conventions — how every endpoint writes times, credits and lists, and how it pages.
  • Errors and limits — the error envelope and what each status means.
  • Text to speech — models, formats, sample rates and streaming.
  • Agents — characters that hold a spoken conversation.
  • API reference — every endpoint and field.