Text to speech

Turn text into spoken audio in a choice of voices, from your agent through the Flux tools server or from your own code, streamed as it is made, on the same Flux key.

Send text and a voice, and get spoken audio back. Speech runs on the same Flux key and base URL (https://api.fluxrouter.ai/v1) as your chat requests, and it lands on the same bill.

Way inStatus
API: POST /v1/audio/speechRolling out
Flux tools server: the speak toolShipping
Voice list: GET /v1/audio/voicesLive

The endpoint is OpenAI-compatible. If it is not switched on for your account yet, a call answers 404 and nothing is billed. Speech needs a paid plan; see What it costs.

For the other direction, audio into text, see Speech to text.

Choose a model and a voice

Two speech models are documented here:

Model idUse it for
flux-voice-speakThe default. Every voice on its list, a speed setting, and instructions to steer tone and delivery.
flux-voice-speak-hdHigher-fidelity audio from a smaller set of the same voices. No instructions.

Every request names a voice, and the voice has to belong to the model you picked. Choose a voice lists the voices, how to read them live for your key, and which ones flux-voice-speak-hd takes.

From your agent

Add the Flux tools server to your agent once, then ask it to speak. The tool is called speak.

Built into Murage, which decides which abilities it uses. Nothing to set up here. See murage.ai.

The tool takes:

ArgumentRequiredNotes
textyesUp to 1,000 characters a call.
voicenoA voice id. The tool picks the model that owns that voice. Defaults to alloy.
formatnomp3 (the default) or wav.
speednoA number; see the speed range under From code.

The agent has to save the audio itself. The tool returns the audio inside its answer as base64, with its mime_type, bytes, billed_chars and cost_usd. Nothing is written to disk until the agent decodes it and writes a file, so say where to put it in your request, as the example does.

The tool's text limit is lower than the API's (4,096 characters) on purpose: base64 audio is large, and a long clip would fill the agent's context. For longer text, have the agent split it into parts and speak each one, or call the API from code.

From code

Send JSON with model, input and voice. The answer is audio bytes, not JSON.

bash
curl https://api.fluxrouter.ai/v1/audio/speech \
  -H "Authorization: Bearer sk-YOUR-FLUX-KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-voice-speak",
    "input": "Your build finished. All tests passed.",
    "voice": "nova",
    "response_format": "mp3"
  }' \
  --output done.mp3

The request fields you will use most:

  • response_format: mp3 (the default), opus, aac, flac, wav or pcm. Use opus for voice notes, mp3 for files people will download, and pcm for the lowest-latency playback.
  • speed: from 0.25 to 4, where 1 is normal speed.
  • instructions (flux-voice-speak only): plain-language direction for delivery, such as "Calm and warm, like a bedtime story" or "Brisk, like a news bulletin". Up to 4,096 characters.
  • input: up to 4,096 characters a request. Split longer text at sentence ends and send each part in turn.

Every field, the content type for each format and the error codes are in the endpoint reference.

Playing a stream as it arrives

The audio streams back as it is made, so you can start playing before the whole clip exists. The first bytes arrive quickly; the rest follows in chunks.

The simplest way to hear it on a desktop is to pipe the stream into a player. ffplay comes with ffmpeg:

bash
curl -sN https://api.fluxrouter.ai/v1/audio/speech \
  -H "Authorization: Bearer $FLUX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"flux-voice-speak","input":"Hello from Flux.","voice":"nova"}' \
  | ffplay -autoexit -nodisp -loglevel quiet -

In your own app, ask for pcm and write each chunk straight to the sound card. pcm is raw audio at 24 kHz, 16-bit, signed little-endian, mono, with no header, so there is nothing to decode:

python
import pyaudio
from openai import OpenAI

client = OpenAI(api_key="sk-YOUR-FLUX-KEY", base_url="https://api.fluxrouter.ai/v1")

player = pyaudio.PyAudio().open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)

with client.audio.speech.with_streaming_response.create(
    model="flux-voice-speak",
    input="Hello from Flux. This starts playing before it has finished.",
    voice="nova",
    response_format="pcm",
) as response:
    for chunk in response.iter_bytes(chunk_size=4096):
        player.write(chunk)

If you play speech in a browser, call Flux from your server and pass the audio on. Never put your Flux key in browser code.

Limits

  • Text: up to 4,096 characters a request on the API, and 1,000 characters a call on the speak tool.
  • Time: if no audio arrives within 30 seconds, the request ends with a 504 and you are not charged. Once audio is flowing, a gap of more than 30 seconds between chunks ends the stream.
  • Rate: up to 60 requests a minute per account, shared with transcription and search, and an hourly audio budget of 36,000 seconds of audio an hour, shared with transcription. Speech draws on that budget by an estimate of how long the text takes to say. Over either, you get 429; wait and retry.

What it costs

Speech is billed per character of input. Price: from $26 per 1M characters. The exact price for each model is on GET /v1/models, in the list_price_microcents field of the flux-voice-speak rows.

  • Slowed speech costs more on flux-voice-speak. With speed below 1, the billed characters are the input length divided by the speed, rounded up, because slower speech makes longer audio. At 1 or faster you pay for the plain character count. flux-voice-speak-hd always bills the plain count.
  • Nothing is billed before the first byte of audio. A request that fails before any audio arrives costs nothing. Once audio has started, the charge stands, even if your client disconnects.

Every successful response carries its receipt in the headers, which arrive with the first byte:

HeaderMeaning
x-flux-routed-modelThe model id that served
x-flux-billed-charsThe characters you were billed for
x-flux-cost-usdWhat this request cost, in US dollars

A key without a paid plan gets 402 with code premium_locked, and no audio is made or billed. See Plans.

Next