Build

Build with audio.

Give your app a voice. Turn recordings into text. Ask questions about what was said.

Three ways to work with sound

Speak

Give your app a voice with a short greeting or narration.

TextAudio file· Per character

Transcribe

Turn a recording into searchable text, with timestamps when available.

RecordingTranscript· Per second

Understand

Ask for a summary, action items or answers about the recording.

Recording + promptModel answer· Prompt + completion tokens

File-based requests, through one OpenAI-compatible API. Speech and transcription return complete files or text, without streaming or realtime.

Start with something useful

Create a key, add credit, and set it in your terminal. Your key must allow the model used in the example.

Set your API key
export MINIROUTER_API_KEY="paste-your-api-key-here"

Choose a recipe, then edit its text or supply your own recording. These are editable code examples.

Each run is a paid request. Keep automatic retries disabled: a timeout or lost response can still incur a charge.

Welcome someone in

Give an onboarding screen a short spoken welcome.

You supply
Text and access to openai/tts-1.
You get
speech.mp3 and response.headers in your current folder.
Welcome someone in
curl --fail-with-body --retry 0 --max-time 120 \
  https://api.minirouter.sh/v1/audio/speech \
  -H "Authorization: Bearer $MINIROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  -D response.headers --output speech.mp3 \
  --data '{
  "model": "openai/tts-1",
  "input": "Welcome back. Your workspace is ready. Let us get started.",
  "voice": "alloy",
  "response_format": "mp3"
}'

Copy a recipe into your terminal. Selecting one here makes no API call.

Keep the input small

Speech input
4,096 characters
Audio upload
8 MiB
Audio duration
3,600 s
Speech & transcription
1 attempt

Upload WAV, FLAC, Ogg, MP4/M4A or MP3. WebM is not supported; Microsoft transcription also excludes MP4/M4A. Speech and transcription do not use Auto, Fusion or fallback model lists.

From hold to settled charge

Credit is reserved before the request. After the provider reports usage, the charge settles and unused credit returns to your balance.

A 2.49-second recording

If the provider reports the same duration, only that usage is charged.

Settled charge
2.49 s × rate
Released credit
0.51 s × rate
  1. ReserveSpeech uses the character count. Transcription rounds the file duration up to a second. Audio adds 32 prompt tokens per second, alongside other input and reserved output tokens.
  2. SettleActual provider usage determines the final charge. The chart illustrates transcription at a per-second rate.
  3. Check Request logsKeep x-minirouter-request-id to find the settled charge. Unused reserved credit is released.

Use the voice catalog for current rates, including the MiniRouter fee.

Pricing fields and receipts

Catalog units, credit holds and Request logs exports.

Speech models price per character (pricing.speech_character in the catalog, shown per 1M characters). Transcription models price per second (pricing.transcription_second, shown per minute). Both list prompt and completion as 0. Prices include the MiniRouter fee.

Speech holds are exact: characters × the per-character rate. Transcription holds read the duration from the uploaded file and reserve it, rounded up to a second, × the per-second rate. Audio contributes 32 prompt tokens per second to the hold, alongside other input and reserved output tokens. Each hold settles to the provider's reported usage and the rest is released. Request logs rows show speech_characters or transcription_seconds with zero tokens; the CSV export carries both columns.

Text to speech

Send text and a voice to /v1/audio/speech. The response is an audio file, ready to save and play.

Speech body, voices and SDKs

6 output formats, provider voices, Node.js and Python.

Body: model, input (up to 4,096 characters), optional voice, response_format (mp3, wav, opus, pcm, aac, flac; mp3 by default), speed and instructions. The response is the audio file with that format's Content-Type. No streaming.

Voices are per provider: OpenAI alloy, ash, coral, echo, fable, nova, onyx, sage, shimmer; Microsoft en-US-Harper, en-US-Nova, en-US-Davis, en-US-Jenny. An unknown voice returns 400.

Text to speech
// npm install openai@7.4.0
import OpenAI from 'openai';
import { writeFile } from 'node:fs/promises';

const client = new OpenAI({
  baseURL: 'https://api.minirouter.sh/v1',
  apiKey: process.env.MINIROUTER_API_KEY,
  maxRetries: 0,
});
const response = await client.audio.speech.create({
  model: 'openai/tts-1',
  input: 'The quick brown fox jumps over the lazy dog.',
  voice: 'alloy',
  response_format: 'mp3',
});
await writeFile('speech.mp3', Buffer.from(await response.arrayBuffer()));
console.log(response.headers.get('x-minirouter-request-id'));
Text to speech
# pip install openai==2.53.0
import os
from openai import OpenAI

client = OpenAI(
    base_url='https://api.minirouter.sh/v1',
    api_key=os.environ['MINIROUTER_API_KEY'],
    max_retries=0,
)
raw = client.audio.speech.with_raw_response.create(
    model='openai/tts-1',
    input='The quick brown fox jumps over the lazy dog.',
    voice='alloy',
    response_format='mp3',
)
with open('speech.mp3', 'wb') as output:
    output.write(raw.content)
print(raw.headers.get('x-minirouter-request-id'))

Speech to text

Upload a recording to /v1/audio/transcriptions. Choose plain text, JSON or metadata with timestamps when available.

Uploads, response formats and SDKs

Multipart or base64, accepted containers, Node.js and Python.

Multipart: file, model, optional language (accepted but currently ignored upstream) and response_format (json default, text, verbose_json). Uploads are capped at 8 MiB.

JSON body: model, audio (base64), media_type, optional language and response_format. The same 8 MiB cap applies to the decoded audio, and a larger body is refused with 413. Write the body to a file so large recordings do not hit the shell's argument limit.

Speech to text
{
  printf '{"model":"openai/whisper-1","media_type":"audio/wav","response_format":"json","audio":"'
  base64 < meeting.wav | tr -d '\n'
  printf '"}'
} > body.json

curl --fail-with-body --retry 0 --max-time 120 \
  https://api.minirouter.sh/v1/audio/transcriptions \
  -H "Authorization: Bearer $MINIROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @body.json

json returns { "text": "…" }. text returns the transcript as plain text. verbose_json returns:

Speech to text
{
  "text": "The quick brown fox jumps over the lazy dog.",
  "language": "en",
  "duration": 2.49,
  "segments": [{ "start": 0, "end": 2.49, "text": "The quick brown fox jumps over the lazy dog." }]
}

language and duration can be null when the provider does not report them. segments can be empty; segment timestamps are included only when available.

Accepted containers: WAV, FLAC, OGG, MP4/M4A, MP3, up to 3,600 s; WebM is rejected. Microsoft transcription also rejects MP4/M4A.

Speech to text
// npm install openai@7.4.0
import OpenAI from 'openai';
import { createReadStream } from 'node:fs';

const client = new OpenAI({
  baseURL: 'https://api.minirouter.sh/v1',
  apiKey: process.env.MINIROUTER_API_KEY,
  maxRetries: 0,
});
const result = await client.audio.transcriptions.create({
  model: 'openai/whisper-1',
  file: createReadStream('meeting.wav'),
  response_format: 'verbose_json',
}).withResponse();
console.log(result.data.text, result.data.duration);
console.log(result.response.headers.get('x-minirouter-request-id'));
Speech to text
# pip install openai==2.53.0
import os
from openai import OpenAI

client = OpenAI(
    base_url='https://api.minirouter.sh/v1',
    api_key=os.environ['MINIROUTER_API_KEY'],
    max_retries=0,
)
with open('meeting.wav', 'rb') as audio:
    raw = client.audio.transcriptions.with_raw_response.create(
        model='openai/whisper-1',
        file=audio,
        response_format='verbose_json',
    )
print(raw.parse().text)
print(raw.headers.get('x-minirouter-request-id'))

Audio input on chat

Attach a recording as a file part with an audio/* data URL. Ask a model that supports audio to summarize it or answer questions.

Responses API and eligible models

input_file on /v1/responses; input_audio is rejected.

Send a recording to a language model as a file content part whose file_data is an audio/* data URL. Only models whose catalog input_modalities include audio accept it, for example google/gemini-3.6-flash and xiaomi/mimo-v2.6-flash. Containers: WAV, FLAC, OGG, MP4/M4A, MP3, up to 3,600 s per request. Audio is billed as ordinary prompt tokens at the model's input rate.

On /v1/responses use an input_file part:

Audio input on chat and responses
{
  printf '{"model":"xiaomi/mimo-v2.6-flash","max_output_tokens":256,"input":[{"role":"user","content":['
  printf '{"type":"input_text","text":"Summarize this recording in one sentence."},'
  printf '{"type":"input_file","filename":"clip.mp3","file_data":"data:audio/mpeg;base64,'
  base64 < clip.mp3 | tr -d '\n'
  printf '"}]}]}'
} > body.json

curl --fail-with-body --retry 0 --max-time 120 \
  https://api.minirouter.sh/v1/responses \
  -H "Authorization: Bearer $MINIROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @body.json

input_audio parts are rejected with 400.

Model availability

Find an eligible model and check your key permissions.

Speech and transcription models are listed in the voice catalog with type speech or transcription. Language models that accept audio list audio in architecture.input_modalities. Your API key must permit the model. Unavailable routes return HTTP 503 before a credit hold or provider call.

Endpoints

Four paths, one bearer key, supported request shapes.
MethodPathFormat
POST/v1/audio/speechOpenAI speech body; raw audio bytes back
POST/v1/audio/transcriptionsOpenAI multipart or a JSON body; text back
POST/v1/chat/completionsfile content part with an audio/* data URL
POST/v1/responsesinput_file part with an audio/* data URL

Use a MiniRouter API key as a bearer token. These examples name a concrete model that accepts the input.

Speech and transcription return complete files or text. These two endpoints do not support streaming, realtime, saved presets, Auto, Fusion or fallback model lists.

Chat and Responses accept inline audio files on eligible models. Saved presets and supported model fallbacks follow the normal routing rules; every selected route must accept audio. input_audio parts, the chat audio field and audio output in modalities are rejected. Audio is not supported with provider keys (BYOK).

Errors

400, 402, 404 and 413, and what each means.
StatusCause
400Invalid body, unknown voice or format, input over 4,096 characters, unsupported container, audio over 3,600 s, input_audio part, a model that does not accept audio
402Insufficient credit for the hold
404Unknown model
413Upload over 8 MiB

Disable automatic retries: a lost response may still incur cost. Keep x-minirouter-request-id and read the settled charge in Request logs.

References

Catalog, authentication, billing and errors.