Audio
Three endpoints in OpenAI's shape. The model is <provider>/<model>, for example
openai/tts-1, openai/whisper-1 or openrouter/openai/whisper-large-v3-turbo.
| Endpoint | Unit | Providers |
|---|---|---|
POST /v1/audio/speech | 1,000 input characters (1,000 UTF-8 bytes on Fish Audio models) | openai, openrouter, elevenlabs, fish-audio, together, nanogpt, pollinations |
POST /v1/audio/transcriptions | minute of audio | openai, groq, openrouter, elevenlabs, fish-audio |
POST /v1/audio/translations | minute of audio | openai, groq |
1curl https://api.aile.sh/v1/audio/speech \2-H "Authorization: Bearer $AILE_KEY" \3-H "Content-Type: application/json" \4-d '{ "model": "openai/tts-1", "voice": "alloy", "input": "Hello from Aile." }' \5--output hello.mp367curl https://api.aile.sh/v1/audio/transcriptions \8-H "Authorization: Bearer $AILE_KEY" \9-F model=openai/whisper-1 \10-F [email protected]
Speech
inputis up to 4,096 characters.stream_format: "audio"streams the audio as it is made.stream_format: "sse"answers onespeech.audio.deltaand aspeech.audio.doneonce the audio is finished.stream: trueis a400. Usestream_format.- OpenRouter speech models take
response_formatmp3(the default) orpcm. - ElevenLabs (
elevenlabs/eleven_v4,eleven_v3,eleven_multilingual_v2,eleven_flash_v2_5,eleven_flash_v2) takes an ElevenLabs voice id asvoice, not OpenAI's voice names; with none it uses its default voice.response_formatismp3,opus,pcmorwav. Its transcription models areelevenlabs/scribe_v2andscribe_v2_medical. - Fish Audio (
fish-audio/s2.1-pro,s2-pro,drama-3) takes a Fish Audio voice id (32 hex characters) asvoice, not OpenAI's voice names; with none it uses Fish Audio's default voice.response_formatismp3,opus,pcmorwav. Its transcription models arefish-audio/transcribe-1-proandtranscribe-1; Pro marks speakers inline as<|speaker:N|>.
Transcription and translation
- Upload is
multipart/form-data, up to 25 MB. OpenRouter's JSON body (input_audio: { data, format }) is accepted too. response_formatisjson(the default),text,verbose_json,srtorvtt. A model that returns onlyjsonstill answersverbose_json, with no segments, and refusestimestamp_granularities[]=word.stream: truereturns the finished transcript astranscript.text.deltaandtranscript.text.doneevents.- A JSON answer's
usageis{ "type": "duration", "seconds": … }, the reported length.
Billing
- Speech bills its input characters. A stream is billed in full once any audio reaches you, and not at all if the provider fails mid-stream.
- Fish Audio models, on
fish-audioand asopenrouter/fish-audio/..., are counted in UTF-8 bytes, as Fish Audio bills them. One unit is 1,000 bytes: plain English is one byte a character, and other scripts use two to four. - Transcription holds an estimate from the upload's size and bills the duration the provider reports, up to the hold.
- A response with no audio, or a transcript with no duration, is a
502and is not charged. - A call with no answer by the deployment's buffered deadline (at most 120 seconds) is voided with a
504. For transcription, the upload's time counts. - A
502or504arrives as503with the real code inx-aile-status. - A provider refusal you can fix (
400,404,413,422…) passes through. One about the sharer's account is a502saying you were not charged. - A key's pins and the routing headers choose the sharer here too.
x-aile-providermust match the model's prefix, andx-aile-max-pricedoes not apply to a per-character or per-minute price. - Where the deployment enables it, speech also takes keyless x402, buffered only (
stream_format: "audio"needs a key). So do transcription and translation, priced on the file's measured length (WAV, MP3, FLAC, OGG/Opus, M4A, WebM or AAC, up to 10 minutes); a file whose length cannot be read needs an API key. If the provider measures the audio more than 2% + 1 second longer than the file's header said, the transcript is withheld with a400and nothing is charged. - Audio is sent to the provider as uploaded. Aile does not inspect it.