Flux Voice: Speech-to-Text on FluxRouter With the Key You Already Have

Point your OpenAI transcription calls at FluxRouter, use flux-voice, done. Same key, same base URL, one new model id. Python + curl, request fields, response formats, costs and limits. Runs on any paid plan.

Cover Image for Flux Voice: Speech-to-Text on FluxRouter With the Key You Already Have

By Sean Donahoe · Published July 16, 2026 · Model names, rates, and limits are accurate as of this date.

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 ten-second billing floor that quietly changes the math 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:

text
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:

bash
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-accurate favors accuracy.
  • flux-voice-fast favors 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.

Want to confirm the ids are live right now? One call returns every usable flux-* id your key can reach:

bash
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:

python
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:

bash
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

FieldRequiredWhat it does
fileYesThe audio. Formats: wav, mp3, mp4/m4a, mpeg/mpga, ogg, webm, flac. Up to 8 MB.
modelNoflux-voice (default, omit to get it), flux-voice-accurate, or flux-voice-fast.
languageNoISO-639-1 code (en, es, de). Omit and it auto-detects.
promptNoA short hint of names, jargon, or spellings to improve accuracy.
response_formatNojson (default), verbose_json, or text.
timestamp_granularities[]Noword and/or segment. Needs verbose_json.
temperatureNoSampling temperature.

What comes back

Three response shapes, and you pick with response_format:

  • json (default) gives you { "text": "..." }.
  • text gives you the transcript as a plain string, nothing to parse.
  • verbose_json adds language, duration, and segments. Ask for timestamp_granularities[]=word and 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:

HeaderWhat it tells you
x-flux-routed-modelWhich variant actually served the clip.
x-flux-billed-secondsSeconds billed on this request.
x-flux-cost-usdWhat it cost, in USD.

Here's a real one off my own probe. I sent a one-second clip. It came back x-flux-routed-model: flux-voice-accurate, x-flux-billed-seconds: 10, x-flux-cost-usd: 0.016670. Ten seconds billed on a one-second clip, because there's a ten-second floor per request (more on that below). Ten seconds at ten cents a minute is about $0.0167, and 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.

python
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 favors latency. Pin -fast and take text.

python
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 8 MB ceiling (413 over it) and the per-account rate limit (429 on a burst), so back off and retry instead of hammering.

python
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:
        print("skip (over 8 MB):", 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.

python
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 audio-minute, charged per second, with a ten-second minimum per request. Same price whichever variant serves the clip. It lands on the same Flux bill as everything else, one invoice, nothing separate to reconcile. That's the rate as of July 16, 2026, and pricing in this space moves, so the live guide is the number that counts on any given day.

The limits worth coding against:

  • Files up to 8 MB. Over that comes back 413.
  • Rate-limited per account. Burst too hard and you get 429, so back off and retry.

And the error map, so your client handles the common cases instead of guessing:

CodeMeansDo this
401Key missing or invalidFix the key.
402 premium_lockedValid key, no paid planUpgrade to a paid plan; Voice turns on.
413File over 8 MBSplit or compress before sending.
429Rate limitedBack off and retry.
400Unsupported optionYou asked for streaming or srt/vtt. Drop it.

Those model ids, that rate, the 8 MB limit, the format list: all current as of July 16, 2026. If you're reading this later, run GET /v1/models and check the guide before you trust the numbers on this page.

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-voice model, the /v1/audio/transcriptions endpoint, same key and base URL, request fields, response formats, the transparency headers, the paid-plan gate, the 8 MB limit, and the $0.10/audio-minute rate: 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.

Right-size every prompt, see what each call costs, and pay only for what you use. That is the kind of thing we built Flux to handle.

One key. Pay only for what you use.