Documentation
Audio & speech
Text-to-speech with POST /v1/audio/speech and speech-to-text with POST /v1/audio/transcriptions: voices, formats, timestamps, and billing.
Two endpoints cover audio. /v1/audio/speech turns text into spoken audio, and /v1/audio/transcriptions turns an uploaded recording into text. Both are synchronous — there is no job to poll, unlike video.
The shapes differ from the rest of the API in one way each: speech returns raw audio bytes rather than JSON, and transcription takes a multipart/form-data upload rather than a JSON body.
POST https://api.infro.io/v1/audio/speech
POST https://api.infro.io/v1/audio/transcriptionsText to speech
modelstringrequiredelevenlabs/turbo-v3,cartesia/sonic-3,minimax/speech-2.6, oropenai/tts-2. Latency, voice range, and language coverage differ sharply — compare them in the catalog.inputstringrequired- The text to speak. Punctuation drives prosody, so keep sentence marks intact; models pause on periods and lift on question marks.
voicestringrequired- Voice ID from the model's voice list. IDs are vendor-specific and are not portable, which is why
fallbacksacross TTS vendors rarely works — a voice that exists on one vendor does not exist on another. speednumber0.5–2.0, default1.0. Applied natively by models that expose a rate control; models without one ignore it.formatstring- Container and codec of the audio:
mp3(default),wav,opus,flac, orpcm(16-bit little-endian, 24 kHz, no header). response_formatstringaudio(default) streams the bytes back with the matchingContent-Type.urlreturns JSON instead —{"url": "...", "usage": {...}}— which is easier when a browser or mobile client fetches the file directly.
curl https://api.infro.io/v1/audio/speech \
-H "Authorization: Bearer $INFRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "elevenlabs/turbo-v3",
"input": "Your deployment finished. Two of forty checks were skipped.",
"voice": "aria",
"format": "mp3"
}' \
--output speech.mp3Streaming playback
The default audio response is chunked: the first bytes arrive while the rest of the sentence is still being synthesized. There is no separate streaming parameter — read the body as a stream instead of buffering it, as the Python tab above does, and playback can start in a few hundred milliseconds.
mp3 and pcm are the practical choices for play-while-downloading. wav writes a length header some players wait for, and flac has to be decoded in full before it is useful, so both are better suited to files you save rather than play live.
Speech to text
filefilerequired- The recording, sent as
multipart/form-data.mp3,wav,m4a,flac,ogg, andwebmare accepted, up to 1 GB. modelstringrequiredopenai/whisper-v3-turbo,deepgram/nova-4, orassemblyai/universal-3. They differ most on diarization quality and on accented speech — see the catalog.languagestring- ISO 639-1 hint, e.g.
enorde. Omit to auto-detect. Passing it when you already know the language cuts latency and measurably improves accuracy on clips under ten seconds. timestampsboolean- When
true, the response includes asegmentsarray withstartandendoffsets in seconds alongside each piece of text. diarizeboolean- When
true, each segment carries aspeakerlabel (speaker_0,speaker_1, …). Impliestimestamps. Models that cannot diarize reject the request with400 invalid_request_errorrather than returning unlabeled segments.
curl https://api.infro.io/v1/audio/transcriptions \
-H "Authorization: Bearer $INFRO_API_KEY" \
-F file=@standup.m4a \
-F model="deepgram/nova-4" \
-F language="en" \
-F timestamps=true \
-F diarize=trueTranscription response
{
"text": "Deploys are green. Two checks were skipped on the mobile build.",
"language": "en",
"model": "deepgram/nova-4",
"provider": "deepgram",
"segments": [
{
"id": 0,
"start": 0.0,
"end": 4.8,
"speaker": "speaker_0",
"text": "Deploys are green."
},
{
"id": 1,
"start": 4.8,
"end": 12.4,
"speaker": "speaker_1",
"text": "Two checks were skipped on the mobile build."
}
],
"usage": {
"seconds": 12.4,
"cost": 0.0014
}
}text is always present and is the full transcript. segments appears only when timestamps (or diarize) is true, and speaker only when diarize is true. provider is the usual INFRO extension naming who served the request.
Set logging: false on either endpoint to keep transcripts, input text, and synthesized audio out of durable storage — only billing metadata is retained. See Privacy & data.
Billing
| Endpoint | Unit | Where cost is reported |
|---|---|---|
/v1/audio/speech | Per 1M characters of input | usage in the JSON body when response_format is url; otherwise the console usage export |
/v1/audio/transcriptions | Per hour of audio, metered by the second | usage.seconds and usage.cost in the response |
Every character of input counts, including whitespace and punctuation. Transcription bills wall-clock duration, so silence and music in a recording cost the same as speech — trim dead air before uploading if you are processing hours of it. Per-model rates are in the catalog and on pricing.
Long recordings
Transcription holds the connection open for the whole job, so very long uploads can hit 408 request_timeout. Split anything past about an hour into chunks on silence boundaries, transcribe them in parallel, and concatenate the text — offsetting each chunk's segments by its start time keeps timestamps continuous. Status codes and retry guidance are in Errors, and per-key throughput in Rate & spend limits.