GET /v1/audio/voices
The exact contract for the Flux voice list: the response schema, how it is filtered by your key, entitlement values, ETag caching and errors.
Lists the speech voices your key can use. It makes no speech and is not billed. For a guided introduction, see Choose a voice.
| Endpoint | GET https://api.fluxrouter.ai/v1/audio/voices |
| Auth | Authorization: Bearer <your Flux key> only; x-api-key is not read |
| Plan | Any valid key |
| Status | Live |
The same route also answers at /audio/voices, without the /v1. It works whether or not speech itself is switched on for your account.
Request
No body and no query parameters. Send If-None-Match with the ETag from an earlier response to get 304 when nothing has changed.
Response
{
"object": "list",
"data": [
{
"id": "coral",
"model": "flux-voice-speak",
"name": "Coral",
"gender": "neutral",
"description": "Standard voice",
"languages": ["en"],
"entitlement": "paid_cleared"
}
]
}
| Field | Type | Meaning |
|---|---|---|
id | string | The voice id. Send it as voice in a speech request. |
model | string | The speech model this voice belongs to. Send it as model with this voice. |
name | string | A display name. |
gender | string | As described for the voice, for example female, male or neutral. |
description | string | A short description of the voice. |
languages | array of strings | Languages the voice is described for, such as en, or multilingual. |
entitlement | string | paid_cleared or premium_locked: whether this key can make speech now. The same for every row in one response. |
Rows are sorted by model, then id. The response can carry other fields; do not depend on any field not listed here.
flux-voice-speak-hd takes a subset of the flux-voice-speak voices and has no rows of its own. Its voices are listed in the speech reference.
Filtering by key
The list holds only voices whose model is on your key's model list, which is the same list GET /v1/models returns for that key. Two keys on one account can see different voices if their model lists differ. Read the list with the key that will make the speech.
Caching
| Header | Value |
|---|---|
ETag | A hash of the response body |
Cache-Control | private, no-cache |
Vary | Authorization |
Store the body and its ETag per key. Before each use, send the ETag back in If-None-Match. A 304 Not Modified has no body: keep your stored copy.
Errors
| Status | Body | Meaning |
|---|---|---|
401 | {"error": {"message": "Unauthorized"}} | No Authorization: Bearer header, or the key was not accepted. |
503 | {"error": {"message": "Voice catalogue unavailable"}} | The list could not be built. Retry shortly. |
Errors carry Cache-Control: no-store.
Example requests
curl -i https://api.fluxrouter.ai/v1/audio/voices \
-H "Authorization: Bearer $FLUX_API_KEY"
import requests
r = requests.get(
"https://api.fluxrouter.ai/v1/audio/voices",
headers={"Authorization": "Bearer sk-YOUR-FLUX-KEY"},
timeout=30,
)
r.raise_for_status()
for voice in r.json()["data"]:
print(voice["model"], voice["id"], voice["name"], voice["entitlement"])
let etag: string | null = null;
let voices: unknown[] = [];
async function loadVoices() {
const res = await fetch("https://api.fluxrouter.ai/v1/audio/voices", {
headers: {
Authorization: "Bearer sk-YOUR-FLUX-KEY",
...(etag ? { "If-None-Match": etag } : {}),
},
});
if (res.status === 304) return voices;
if (!res.ok) throw new Error(`voices: ${res.status}`);
etag = res.headers.get("etag");
voices = (await res.json()).data;
return voices;
}