Skip to content
INFRO

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.

Endpoints
POST https://api.infro.io/v1/audio/speech
POST https://api.infro.io/v1/audio/transcriptions

Text to speech

modelstringrequired
elevenlabs/turbo-v3, cartesia/sonic-3, minimax/speech-2.6, or openai/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 fallbacks across TTS vendors rarely works — a voice that exists on one vendor does not exist on another.
speednumber
0.52.0, default 1.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, or pcm (16-bit little-endian, 24 kHz, no header).
response_formatstring
audio (default) streams the bytes back with the matching Content-Type. url returns 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.mp3

Streaming 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, and webm are accepted, up to 1 GB.
modelstringrequired
openai/whisper-v3-turbo, deepgram/nova-4, or assemblyai/universal-3. They differ most on diarization quality and on accented speech — see the catalog.
languagestring
ISO 639-1 hint, e.g. en or de. 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 a segments array with start and end offsets in seconds alongside each piece of text.
diarizeboolean
When true, each segment carries a speaker label (speaker_0, speaker_1, …). Implies timestamps. Models that cannot diarize reject the request with 400 invalid_request_error rather 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=true

Transcription response

json
{
  "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

EndpointUnitWhere cost is reported
/v1/audio/speechPer 1M characters of inputusage in the JSON body when response_format is url; otherwise the console usage export
/v1/audio/transcriptionsPer hour of audio, metered by the secondusage.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.