Speech & Transcription

Two endpoints, both shaped like OpenAI's, so an existing client works by changing the base URL: /audio/speech turns text into audio, and /audio/transcriptions turns audio into text.

Text to speech

POST /api/v1/audio/speech responds with the audio itself, not JSON, so you can pipe it straight into a file or a player.

Bash
curl https://mume.ai/api/v1/audio/speech \ -H "Authorization: Bearer $MUME_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "microsoft/mai-voice-2", "voice": "en-US-Harper", "input": "The build finished, and every test passed." }' \ --output reply.mp3

Parameters

FieldRequiredNotes
modelYesAny model whose surface is speech. See the catalogue.
inputYesThe text to read. Must be non-empty.
voiceYesA voice the model actually has. This is required here even though some upstreams treat it as optional — see the note below.
response_formatNoDefaults to mp3. The response's Content-Type tells you what you actually got.
storeNoDefaults to on. Set false to skip saving the clip to your media library.
streamNoDefaults to off. Set true to receive the audio as it is synthesised — see below.

Streaming

Most speech models generate audio incrementally, and stream: true passes that through instead of waiting for the clip to finish. It is the same response — same headers, same bytes, same price — the first of them just arrives much sooner. On deepgram/aura-2, a paragraph that takes 32 seconds to synthesise starts arriving after 2.

Bash
curl https://mume.ai/api/v1/audio/speech \ -H "Authorization: Bearer $MUME_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "deepgram/aura-2", "voice": "aura-2-thalia-en", "input": "Streaming starts playing before synthesis has finished.", "stream": true }' \ --no-buffer --output - | ffplay -nodisp -

Not every model streams: some synthesise the whole clip upstream and send it in one piece. Asking for a stream from one of those is harmless — you get the same audio, all at once — so there is nothing to look up and no reason to special-case it.

A failure after the first byte closes the connection. The status line has already been sent by then, so there is no error code left to give you. A truncated transfer is the signal; treat a clip that ends early as failed rather than short. Nothing else changes — X-Media-Url is still sent up front, and still points at the complete clip once it lands.

A voice belongs to a model

Voice names are not portable — en-US-Harper exists on one model and not on another — so the gateway requires voice rather than choosing for you. Omitting it upstream produces a schema error that reads like a problem with your audio settings, which is a confusing way to learn that you forgot a field.

Match the voice to the language of the text. Nothing stops a Spanish voice reading French, and the result is the most common way this goes wrong.

Response headers

A speech response is the audio, so there is nowhere in the body to report a cost or an id. Both come back as headers — and because they are headers, they are already there when a streamed clip starts arriving:

HeaderMeaning
X-Generation-IdIdentifies the clip. Always present.
X-Media-UrlThe hosted copy in your media library. Absent when you passed store: false.

Speech to text

POST /api/v1/audio/transcriptions accepts either the OpenAI multipart shape or a JSON body carrying a URL. Both return the same thing.

Multipart, with a file

Bash
curl https://mume.ai/api/v1/audio/transcriptions \ -H "Authorization: Bearer $MUME_API_KEY" \ -F model="openai/whisper-1" \ -F file=@interview.mp3

JSON, with a URL

The JSON form exists because callers often already hold a URL — a file in their own storage, or something a user uploaded. Downloading it only to upload it again through us is pure waste.

Bash
curl https://mume.ai/api/v1/audio/transcriptions \ -H "Authorization: Bearer $MUME_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "openai/whisper-1", "file_url": "https://example.com/interview.mp3" }'

Response

JSON
{ "text": "So the thing about the migration is that we never turned the old path off…", "usage": { "seconds": 184.2, "cost": 0.0011 } }

Limits

  • 25 MB per file. Check the size before you send — a rejected upload costs you the round trip.
  • Container formats the model accepts go straight through, including mp4 and webm. The gateway does not transcode, so it cannot rescue a format the model refuses.

Errors

Both endpoints use the shared error shape — see Error Handling. The two you will meet first are invalid_request for a missing model, input or voice, and 402 when the account is out of credit.

Related

  • Images & Video — the other two media surfaces, which bill the same way.
  • Voice-over Translator — an agent built on both of these endpoints, if you would rather not wire them together yourself.