Speech & transcription
OpenAI-compatible text-to-speech and speech-to-text, priced per character, per second, or per audio token.
Gopura serves the two OpenAI audio endpoints, so client.audio.speech.create
and client.audio.transcriptions.create in any OpenAI SDK work after the base
URL swap:
POST /v1/audio/speechturns text into audio (text-to-speech).POST /v1/audio/transcriptionsturns audio into text (speech-to-text).
Speech models show modality: "speech" on /v1/models and transcription
models show modality: "transcription". The catalog includes OpenAI TTS and
Whisper, Gemini 3.8 TTS and 3.5 Transcribe, Microsoft MAI-Voice and
MAI-Transcribe, Grok, and Fish Audio.
Text to speech
curl -s https://api.gopura.net/v1/audio/speech \
-H "Authorization: Bearer $GOPURA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/tts-1",
"input": "Welcome to Gopura.",
"voice": "nova",
"response_format": "mp3"
}' -o welcome.mp3
Or with the SDK:
const speech = await client.audio.speech.create({
model: 'openai/tts-1',
input: 'Welcome to Gopura.',
voice: 'nova',
});
await fs.promises.writeFile('welcome.mp3', Buffer.from(await speech.arrayBuffer()));
Request
| Field | Meaning |
|---|---|
model | a speech model id from the catalog |
input | the text to speak, up to 4,096 characters |
voice | the provider's voice id; omit it to get the model's default voice |
response_format | mp3, opus, aac, flac, wav or pcm |
speed | 0.25–4.0 (OpenAI models) |
instructions | a speaking-style prompt (Gemini TTS) |
language | Gopura extension: a language hint for multilingual voices |
Each model's supported_parameters on /v1/models lists which of these
optional fields it honours.
Voices.
- OpenAI models:
alloy,ash,coral,echo,fable,nova,onyx,sage,shimmer. - Gemini TTS: Google's 30 prebuilt voices, such as
Kore,PuckandZephyr. - Other providers take their own voice ids unchanged.
An unknown voice returns a 400 that includes the provider's message, and
for OpenAI that message lists the valid voices.
Response
The body is the raw audio file, exactly as OpenAI returns it. Because the body is binary, the billing details come back as headers:
| Header | Meaning |
|---|---|
Content-Type | the format you actually received (audio/mpeg, audio/wav, audio/ogg, audio/aac, audio/flac, audio/pcm) |
X-Gopura-Cost | the exact charge in USD |
X-Gopura-Characters | characters in input |
X-Gopura-Input-Tokens / X-Gopura-Output-Tokens | token-priced models only |
X-Gopura-Warning | anything the provider ignored or substituted |
Content-Type is read from the audio itself, not copied from your request.
Gemini TTS only produces WAV (24 kHz, 16-bit mono). If you ask it for mp3
you get WAV, and the response says so:
Content-Type: audio/wav
X-Gopura-Warning: Unsupported output format: mp3. Using wav instead.
pcm is raw 16-bit little-endian samples at 24 kHz with no header.
Transcription
Upload the file as multipart, exactly as with OpenAI:
curl -s https://api.gopura.net/v1/audio/transcriptions \
-H "Authorization: Bearer $GOPURA_API_KEY" \
-F model=openai/whisper-1 \
-F file=@meeting.mp3 \
-F language=en
const transcript = await client.audio.transcriptions.create({
model: 'openai/whisper-1',
file: fs.createReadStream('meeting.mp3'),
});
// transcript.text, transcript.usage.cost
If your client can't send multipart, send JSON with the audio as a base64 data URL instead:
{ "model": "openai/gpt-4o-mini-transcribe", "file": "data:audio/mpeg;base64,SUQzBAAAAAAA…" }
Request
| Field | Meaning |
|---|---|
model | a transcription model id from the catalog |
file | the audio file (mp3, mp4/m4a, wav, webm, ogg, flac, aac), up to 25 MB |
language | ISO-639-1 hint, e.g. hi (OpenAI models) |
prompt | vocabulary or style hint, e.g. product names (OpenAI models) |
response_format | json (default), text or verbose_json |
language and prompt currently reach only the OpenAI models. If you send
them to another model, the request still succeeds and an X-Gopura-Warning
header says which ones were ignored, so they are never dropped silently.
Response
{
"text": "Hello from Gopura. This is a voice test.",
"usage": { "type": "duration", "seconds": 3, "cost": 0.000375 }
}
verbose_json adds language, duration, and segments[], each with
{id, start, end, text}, where the model provides them. With
response_format: "text" the body is plain text and the charge is in the
X-Gopura-Cost header.
Pricing
Every model is priced in the unit its provider bills in. Our margin is
already included in every price on /v1/models, and the charge is returned
with each response, so you never have to reconstruct it.
Pricing on /v1/models | Used by | How the charge is calculated |
|---|---|---|
pricing.character | OpenAI TTS, MAI-Voice, Grok TTS, Fish Audio | characters in input, counted as UTF-16 code units (so an emoji counts as 2) |
pricing.second | Whisper, MAI-Transcribe, Grok STT, Fish Transcribe | audio duration, rounded up to the next whole second |
pricing.prompt / .completion | Gemini TTS, Gemini Transcribe, GPT-4o Transcribe | text and audio tokens; audio is about 25 tokens per second on Gemini |
Some reference points at today's prices:
openai/tts-1: $18.75 per million characters.google/gemini-3.8-flash-tts: about $0.017 per minute of generated speech.openai/whisper-1: $0.0075 per minute.microsoft/mai-transcribe-2: about $0.125 per hour.
Speech is billed per character, so its charge is known before the request runs. A transcription's duration is only known once the provider reports it, so the gateway holds an upper bound first and charges the reported duration. For WAV uploads the bound is exact, read from the file header.
Languages
Gemini 3.8 TTS, MAI-Voice and the OpenAI models are multilingual. We have
verified Hindi end to end, both synthesising it and transcribing it back,
on openai/tts-1, openai/whisper-1, openai/gpt-4o-mini-transcribe and
google/gemini-3.5-transcribe. Check a sample in your own target language
before you rely on it.
Limits and latency
- Speech: 4,096 characters per request. Split longer text at sentence boundaries.
- Transcription: 25 MB per file.
- The gateway gives speech 120 seconds and transcription 10 minutes. A
hung provider returns a clean
504 timeout, and you are not billed. - Transcription runs at roughly 15 seconds per minute of audio: a 6-minute recording took about 90 s on Whisper. Set your client timeout to match, or split long recordings and transcribe the pieces in parallel.
- Measured on single samples on 2026-10-11, so treat these as indicative:
- Speech takes about 1–2 s on every model.
- Transcribing a 4-second clip takes about 1 s on Whisper, GPT-4o mini, Grok and Fish, and about 2 s on Gemini.
microsoft/mai-transcribe-2is the cheapest but took about 22 s for a 4-second clip.
Not yet available
The following are not available yet:
- Streaming speech output (
stream_format: "sse"). - Streaming or realtime transcription (
stream: true; the-liveand-streamingmodel variants). - Realtime voice-agent sessions.
srtandvttsubtitle formats.
A request that asks for one of these gets a clear 400 rather than being
silently ignored.