Speech to text
Turn speech in an audio file into text with the flux-voice models, from your agent through the Flux tools server or from your own code, on the same Flux key as your chat requests.
Send an audio file and get the words back as text. Transcription 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 in | Status |
|---|---|
API: POST /v1/audio/transcriptions | Live |
Flux tools server: the transcribe tool | Shipping |
The endpoint is OpenAI-compatible, so if your code already calls an OpenAI-compatible transcription endpoint, moving to Flux is a base URL and key change. Transcription needs a paid plan; see What it costs.
Pick a model id
Set model to one of three ids. Leave it out and you get flux-voice.
| Model id | What it does |
|---|---|
flux-voice | The default. Picks per clip: it favours accuracy for clips of 10 seconds or less, and speed for longer ones. |
flux-voice-accurate | Always favours accuracy. |
flux-voice-fast | Always favours speed. |
flux-voice never transcribes worse than flux-voice-accurate on short clips; it only decides when speed is worth more. Pin one of the other two only when you have a reason, such as a latency budget. The price is the same whichever serves.
A model id the endpoint does not know is treated as flux-voice, not refused. That keeps SDKs that send another provider's model id working, but it also means a typo silently gets the default. The x-flux-routed-model response header always names the id that actually served.
From your agent
Add the Flux tools server to your agent once, then ask it to transcribe. The tool is called transcribe.
Built into Murage, which decides which abilities it uses. Nothing to set up here. See murage.ai.
Built into Wayland, which decides which abilities it uses. Nothing to set up here. See getwayland.com.
Add the Flux tools server from your terminal.
claude mcp add --transport http flux-tools https://api.fluxrouter.ai/mcp-tools/ \
--header "Authorization: Bearer sk-YOUR-FLUX-KEY"
Then ask Claude Code:
Transcribe meeting.m4a with the Flux transcribe tool and save the text next to it as meeting.txt.
From Claude Code's own docs, checked 2026-09-28.
Add the Flux tools server to the same config. Codex reads the key from FLUX_API_KEY, which you exported above. File: ~/.codex/config.toml
[mcp_servers.flux-tools]
url = "https://api.fluxrouter.ai/mcp-tools/"
bearer_token_env_var = "FLUX_API_KEY"
Then ask Codex:
Transcribe meeting.m4a with the Flux transcribe tool and save the text next to it as meeting.txt.
From Codex's own docs, checked 2026-09-28.
Add the Flux tools server to your global MCP config, then restart Cursor. File: ~/.cursor/mcp.json
{
"mcpServers": {
"flux-tools": {
"url": "https://api.fluxrouter.ai/mcp-tools/",
"headers": {
"Authorization": "Bearer sk-YOUR-FLUX-KEY"
}
}
}
}
Then ask Cursor:
Transcribe meeting.m4a with the Flux transcribe tool and save the text next to it as meeting.txt.
From Cursor's own docs, checked 2026-09-28.
Add the Flux tools server to the same config, then restart Hermes. File: ~/.hermes/config.yaml
mcp_servers:
flux-tools:
url: "https://api.fluxrouter.ai/mcp-tools/"
headers:
Authorization: "Bearer sk-YOUR-FLUX-KEY"
Then ask Hermes:
Transcribe meeting.m4a with the Flux transcribe tool and save the text next to it as meeting.txt.
From Hermes's own docs, checked 2026-09-28.
Not checked yet. We have not checked how OpenClaw adds an MCP server, so we do not show a recipe. See OpenClaw's own docs.
Open Cline's MCP servers panel, edit the settings file, and add the Flux tools server.
{
"mcpServers": {
"flux-tools": {
"type": "streamableHttp",
"url": "https://api.fluxrouter.ai/mcp-tools/",
"headers": {
"Authorization": "Bearer sk-YOUR-FLUX-KEY"
}
}
}
}
Then ask Cline:
Transcribe meeting.m4a with the Flux transcribe tool and save the text next to it as meeting.txt.
From Cline's own docs, checked 2026-09-28.
The tool takes exactly one of these:
audio_url: a publichttps://address. Flux downloads it for you, following up to 3 redirects. Private, local and plainhttp://addresses are refused.audio_base64: the file itself, base64-encoded, with an optionalfilename. Use this for a file on your own machine.
It also takes an optional language and prompt, the same as the API. It returns text, billed_seconds and cost_usd. The file limit is 8 MB either way.
From code
Send the file as multipart/form-data with a file field. OpenAI-compatible SDKs do this for you through their audio transcription method.
curl https://api.fluxrouter.ai/v1/audio/transcriptions \
-H "Authorization: Bearer sk-YOUR-FLUX-KEY" \
-F "file=@meeting.m4a" \
-F "model=flux-voice"
from openai import OpenAI
client = OpenAI(
api_key="sk-YOUR-FLUX-KEY",
base_url="https://api.fluxrouter.ai/v1",
)
with open("meeting.m4a", "rb") as audio:
result = client.audio.transcriptions.create(
model="flux-voice",
file=audio,
)
print(result.text)
import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "sk-YOUR-FLUX-KEY",
baseURL: "https://api.fluxrouter.ai/v1",
});
const result = await client.audio.transcriptions.create({
model: "flux-voice",
file: fs.createReadStream("meeting.m4a"),
});
console.log(result.text);
The full list of request fields and the exact response shapes are in the endpoint reference.
Response formats and timestamps
Choose the shape of the answer with response_format:
json(the default):{"text": "..."}.text: the transcript as a plain text body, not JSON.verbose_json:textplustask,language,durationandsegments, each segment with its start and end time.
For word-level timestamps, send response_format=verbose_json with timestamp_granularities[]=word. The response then adds a words list, which helps with captions, cursor placement in dictation apps and word-level editing.
curl https://api.fluxrouter.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $FLUX_API_KEY" \
-F "file=@note.ogg" \
-F "model=flux-voice" \
-F "response_format=verbose_json" \
-F "timestamp_granularities[]=word"
Subtitle formats (srt, vtt) are not offered, and there is no streaming: the whole transcript comes back in one response. Build subtitles from the verbose_json segments instead.
Languages
Transcription handles most major languages and detects the language by itself. Pass language with an ISO-639-1 code (for example en or de) to pin one, which helps with short clips and mixed-language audio. verbose_json reports the language it heard.
To help with names, jargon or spellings, pass a short prompt, such as a list of product names. Prompts longer than 2,000 characters are cut to that length.
Audio formats
Flux reads the format from the file's contents, not its name or extension. It accepts wav, mp3, mp4 and m4a, ogg (including Opus), webm and flac. A file that is not one of these is refused with 400 unsupported_file_format before anything is billed.
For audio recorded in a browser, the default webm or an ogg/Opus recording works as it is. There is no need to convert it.
Limits
- File size: up to 8 MB a request. Larger files get
413 file_too_large. Split long recordings into parts. - Time: a transcription that takes longer than 60 seconds ends with a
504, and you are not charged. - Rate: up to 60 requests a minute per account, shared with speech and search, and an hourly audio budget of 36,000 seconds of audio an hour, shared with speech. Over either, you get
429; wait and retry.
What it costs
Transcription is billed per second of audio, rounded up, with a minimum of 10 seconds a file. Price: $0.10 per minute heard. It rolls up to the same bill as the rest of your Flux usage.
Every successful response carries its receipt in the headers:
| Header | Meaning |
|---|---|
x-flux-routed-model | The model id that served: flux-voice, flux-voice-accurate or flux-voice-fast |
x-flux-billed-seconds | The seconds you were billed for |
x-flux-cost-usd | What this request cost, in US dollars |
The live price for each model id is on GET /v1/models, in the list_price_microcents and min_billed_units fields of the flux-voice rows. See How billing works.
A key without a paid plan gets 402 with code premium_locked, and nothing is sent for transcription or billed. A 401 means the key itself was not accepted. Handle the two separately in a client: a 402 is a prompt to upgrade, not a broken key. See Plans.
Next
- Talk to your agent: speech in, an answer, and speech out.
- Text to speech, the other direction.
- Speech to text problems, keyed by status code.