By Sean Donahoe · Published 16 July 2026 · Updated 28 September 2026. Prices and limits on this page are read live from the Flux catalogue. For everything your key can do with voices, see Voices.
How do I get speech-to-text on FluxRouter?
Speech-to-text on FluxRouter runs through the flux-voice model on the OpenAI-compatible /v1/audio/transcriptions endpoint. Same key, same base URL (https://api.fluxrouter.ai/v1) you already use for chat. If you call OpenAI-style transcription today, it's a base-URL-and-key change plus one model string. Voice runs on any paid plan.
That's the whole setup. If your code already hits audio.transcriptions.create somewhere, you're minutes from your first transcript through Flux.
What's worth staying for is the part the setup doesn't tell you: the per-request billing minimum that quietly changes the maths on short clips, when it's worth pinning the accurate variant, and the errors you'll want your client to catch before they surprise you in production.
The easy button
Don't want to read a guide? Hand this to your coding agent and let it do the swap:
Point my OpenAI transcription call at FluxRouter. Base URL
https://api.fluxrouter.ai/v1, my Flux key, model flux-voice. Keep
everything else exactly the same.
That's it. No config files, no rollback surface to babysit. Nothing on your machine changes except one base URL and one model string.
Or do it by hand in one command:
curl https://api.fluxrouter.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $FLUX_API_KEY" \
-F model="flux-voice" \
-F file="@meeting.m4a" \
-F response_format="text"
One gate before you copy that: Voice runs on a paid plan. A key without one comes back with 402 and the code premium_locked. The OpenAI SDK raises on that, so catch it and send the user to upgrade instead of letting it throw. Read 402 as "turn this on," not a dead end. A 401 means the key itself is missing or bad.
The model id
Use flux-voice as your model. Your text aliases (flux-fast, flux-standard, flux-reasoning) route to text models; flux-voice routes to a speech-to-text model behind the same key and the same endpoint.
flux-voice auto-balances speed and accuracy per clip. Omit model entirely and you get that default.
Two variants you can pin when you want to force the call yourself:
flux-voice-accuratefavours accuracy.flux-voice-fastfavours latency.
Same paid-plan gate on all three.
Rule of thumb: leave it on the default and let flux-voice balance speed and accuracy for each clip. Pin a variant only when you've got a specific reason to force one.
One more thing worth knowing: a model id the endpoint does not recognise is treated as flux-voice, not refused. That keeps SDKs that send another provider's model id working, but a typo silently gets the default, so read the x-flux-routed-model header to see what served.
Want to confirm the ids are live right now? One call returns every usable flux-* id your key can reach:
curl -s https://api.fluxrouter.ai/v1/models \
-H "Authorization: Bearer $FLUX_API_KEY"
That list is the always-current authority. A blog post can go stale, so when the two disagree, trust the call.
Transcribe a file
The request is an OpenAI transcription request. A file, and optionally a model, sent as multipart/form-data. The OpenAI SDK ships it for you through audio.transcriptions.create, so on an existing OpenAI setup the base URL is the line that changes.
Python, OpenAI SDK:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.fluxrouter.ai/v1", # the one line that changes
api_key=os.environ["FLUX_API_KEY"],
)
with open("meeting.m4a", "rb") as audio:
result = client.audio.transcriptions.create(
model="flux-voice",
file=audio,
)
print(result.text)
Same request as raw curl:
curl https://api.fluxrouter.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $FLUX_API_KEY" \
-F model="flux-voice" \
-F file="@meeting.m4a"
Base URL, key, model, file. That's the job.
Request fields
| Field | Required | What it does |
|---|---|---|
file | Yes | The audio. Formats: wav, mp3, mp4/m4a, ogg (including Opus), webm, flac. Up to 8 MB. |
model | No | flux-voice (default, omit to get it), flux-voice-accurate, or flux-voice-fast. |
language | No | ISO-639-1 code (en, es, de). Omit and it auto-detects. |
prompt | No | A short hint of names, jargon, or spellings to improve accuracy. |
response_format | No | json (default), verbose_json, or text. |
timestamp_granularities[] | No | word and/or segment. Needs verbose_json. |
temperature | No | Sampling temperature. |
What comes back
Three response shapes, and you pick with response_format:
json(default) gives you{ "text": "..." }.textgives you the transcript as a plain string, nothing to parse.verbose_jsonaddslanguage,duration, andsegments. Ask fortimestamp_granularities[]=wordand you get per-word timestamps too.
Two things it won't do, and both return 400: streaming (stream: true) and subtitle formats (srt, vtt). If you need captions, take verbose_json with word timestamps and build the file your side.
Read the headers
Every successful response carries three transparency headers. Log them from day one:
| Header | What it tells you |
|---|---|
x-flux-routed-model | Which variant actually served the clip. |
x-flux-billed-seconds | Seconds billed on this request. |
x-flux-cost-usd | What it cost, in USD. |
Here's a real one off my own probe in July. I sent a one-second clip. It came back x-flux-routed-model: flux-voice-accurate, x-flux-billed-seconds: 10 and a matching x-flux-cost-usd. Ten seconds billed on a one-second clip, because each request has a billing minimum (more on that below). The exact charge is right there on the header, so you never have to back it out yourself.
Now that it's wired
The pipe works. Here's what to actually do with it. Copy-paste, all runnable. Each one reuses the client from the transcribe example above; the batch loop sets it up in full.
1. Transcribe a meeting into timestamped, jump-to notes. Ask for verbose_json with word timestamps, then roll the words up into short lines, each stamped with the second it starts, so you can drop a ?t= link straight to that moment in the recording.
with open("standup.m4a", "rb") as audio:
result = client.audio.transcriptions.create(
model="flux-voice",
file=audio,
response_format="verbose_json",
timestamp_granularities=["word"],
)
def stamp(sec): # 95.0 -> "1:35" (and a ?t=95 you can link to)
return f"{int(sec) // 60}:{int(sec) % 60:02d}"
line, start = [], None
for w in result.words:
if start is None:
start = w.start
line.append(w.word)
if w.word.endswith((".", "?", "!")) or len(line) >= 12:
print(f"[{stamp(start)}] " + " ".join(line))
line, start = [], None
2. A quick voice memo to text, fast. No structure, just the words, and it favours latency. Pin -fast and take text.
result = client.audio.transcriptions.create(
model="flux-voice-fast",
file=open("memo.ogg", "rb"),
response_format="text",
)
print(result)
3. Batch a whole folder. Loop a directory, transcribe each file, write the text next to it. Mind the file size limit (413 over it) and the per-account rate limit (429 on a burst), so back off and retry instead of hammering.
import os, time, pathlib
from openai import OpenAI
client = OpenAI(
base_url="https://api.fluxrouter.ai/v1",
api_key=os.environ["FLUX_API_KEY"],
)
def transcribe(path):
delay = 1.0
while True:
try:
with open(path, "rb") as f:
return client.audio.transcriptions.create(model="flux-voice", file=f)
except Exception as e: # 429 = rate limited
if "429" in str(e) and delay <= 16:
time.sleep(delay) # back off, then retry
delay *= 2
continue
raise
for path in pathlib.Path("audio/").glob("*.m4a"):
if path.stat().st_size > 8 * 1024 * 1024: # check the current limit in the docs
print("skip (over the size limit):", path.name)
continue
result = transcribe(path)
path.with_suffix(".txt").write_text(result.text)
print("done:", path.name)
time.sleep(0.5) # gentle between files
4. Nail the jargon and the names. Feed the odd spellings in through prompt, and pin -accurate when getting them right matters more than shaving latency.
result = client.audio.transcriptions.create(
model="flux-voice-accurate",
file=open("call.wav", "rb"),
prompt="Acme, Kubernetes, Dr. Nguyen",
)
print(result.text)
One note while you're wiring these up: if your audio comes from a browser MediaRecorder, it usually arrives as an ogg/opus blob, and that's a supported format. Send it straight through on flux-voice, no transcoding step first.
Deeper dictation-UI and prompt-pack patterns are their own piece. This gets you transcribing today.
What it costs, and the limits
Billing is simple. $0.10 per minute heard, charged per second of audio and rounded up, with a minimum of 10 seconds a file. Same price whichever variant serves the clip. It lands on the same Flux bill as everything else, one invoice, nothing separate to reconcile.
The limits worth coding against:
- Files up to 8 MB. Over that comes back
413 file_too_large. - A transcription that takes longer than 60 seconds ends with a
504, and you are not charged. - Up to 60 requests a minute per account, shared with speech and search, plus an hourly audio budget shared with speech. Over either, you get
429, so back off and retry.
And the error map, so your client handles the common cases instead of guessing:
| Code | Means | Do this |
|---|---|---|
401 | Key missing or invalid | Fix the key. |
402 premium_locked | Valid key, no paid plan | Upgrade to a paid plan; Voice turns on. |
413 file_too_large | File over the size limit | Split or compress before sending. |
429 | Rate limited | Back off and retry. |
400 | Unsupported option or format | You asked for streaming or srt/vtt, or sent a format that isn't on the list (unsupported_file_format). Fix it and resend. |
The price and limits above are read live, so they stay current. The model ids and the format list were checked on 28 September 2026; GET /v1/models and the guide are the final word.
Receipts
Every claim here traces to a public surface. Run the curl commands yourself; the live API is the final word.
FluxRouter (published docs and live API):
- The
flux-voicemodel, the/v1/audio/transcriptionsendpoint, same key and base URL, request fields, response formats, the transparency headers, the paid-plan gate, the limits and the price: Transcribe audio guide - The live, always-current id list (
flux-voice,flux-voice-accurate,flux-voice-fast):GET https://api.fluxrouter.ai/v1/models - The full id scheme and how the voice ids sit next to the text ones: Models
- Getting a key and your first call: Quickstart
Compatibility note: the request and response shape follow OpenAI's transcription API, which is why pointing an OpenAI-style client at Flux is a base-URL-and-key change.
Ready to wire it up? Grab a key from the quickstart, open the transcribe-audio guide, and remember Voice runs on a paid plan.
