Docs /Getting Started/Streaming and audio formats

Streaming and audio formats

Choose response framing without confusing streamed transport with live generation.

Two separate streaming questions

Model generation asks whether inference emits audio before synthesis completes. Featherless transport asks whether those bytes are returned as bulk binary, one JSON envelope, or SSE events. A streamed Featherless response does not by itself prove live model generation.

Bulk binary

Bulk binary is the default and is the closest OpenAI-compatible mode. Audio bytes are written directly to the response with the actual audio MIME type. Use delivery: bulk and encoding: binary, or omit both fields.

JSON

Use delivery: json when you need the actual format and terminal usage in one response. The complete clip is buffered before its base64 envelope is emitted. Legal leading JSON whitespace may be sent as a keepalive while generation is pending.

Server-sent events

Every example on this page reads your key from the FEATHERLESS_API_KEY environment variable, so export it once before running any of them. The Python examples need the requests library: install it with pip install requests. The TypeScript examples use top-level await, so save one with an .mjs extension (or set "type": "module" in package.json) and run it on Node 18 or newer, which supplies the built-in fetch they rely on.

Consume an SSE audio stream
# Requires jq and openssl.
audio_tmp="$(mktemp)"
done_tmp="${audio_tmp}.done"
trap 'rm -f "$audio_tmp" "$done_tmp"' EXIT

set +e
curl --fail-with-body --no-buffer https://api.featherless.ai/v1/audio/speech \
  --header "Authorization: Bearer $FEATHERLESS_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Accept: text/event-stream" \
  --data '{
    "model": "hexgrad/Kokoro-82M",
    "input": "Deliver the audio as SSE.",
    "voice": "af_bella",
    "response_format": "wav",
    "delivery": "stream"
  }' |
(
  set -euo pipefail
  event_name=""
  data_lines=""

  while IFS= read -r line || [[ -n "$line" ]]; do
    line="${line%$'\r'}"
    if [[ -z "$line" ]]; then
      if [[ -n "$data_lines" ]]; then
        case "$event_name" in
          speech.audio.delta)
            printf '%s' "$data_lines" |
              jq -er '.audio' |
              openssl base64 -d -A >> "$audio_tmp"
            ;;
          speech.audio.done)
            printf '%s' "$data_lines" | jq -e '.usage' > /dev/null
            : > "$done_tmp"
            ;;
        esac
      fi
      event_name=""
      data_lines=""
    elif [[ "$line" == event:* ]]; then
      event_name="${line#event:}"
      event_name="${event_name# }"
    elif [[ "$line" == data:* ]]; then
      data_part="${line#data:}"
      data_part="${data_part# }"
      if [[ -n "$data_lines" ]]; then
        data_lines+=$'\n'
      fi
      data_lines+="$data_part"
    fi
  done
)
pipeline_status=("${PIPESTATUS[@]}")
set -e

if (( pipeline_status[0] != 0 || pipeline_status[1] != 0 )); then
  echo "The SSE request or parser failed." >&2
  exit 1
fi
if [[ ! -f "$done_tmp" ]]; then
  echo "The stream closed before speech.audio.done." >&2
  exit 1
fi

mv "$audio_tmp" speech.audio
rm -f "$done_tmp"
trap - EXIT
event: speech.audio.delta
data: {"audio":"<base64 chunk>"}

event: speech.audio.done
data: {"usage":{"input_characters":25,"output":[{"unit":"byte","quantity":42144}]}}

Concatenate decoded delta bytes in event order. Chunk boundaries are arbitrary transport boundaries, not guaranteed codec frames. A complete response ends with speech.audio.done. Treat a closed connection without that event as failure.

Deferred streaming

Some models finish generation before Featherless opens the resulting audio file. SSE for those models streams delivery of an already completed clip; it does not reduce time to first generated audio. Model guides label this as deferred streaming. Other response bodies can be relayed progressively; each model guide states which behavior applies.

Formats and MIME types

MP3 uses audio/mpeg, Opus uses audio/ogg, AAC uses audio/aac, FLAC uses audio/flac, WAV uses audio/wav, and raw PCM uses audio/pcm. The voices endpoint reports the response-format requests accepted for a model. The current PCM response does not carry sample rate, channel count, or bit depth, so it is not safely self-describing; prefer WAV unless the model's PCM geometry is known out of band.

Requested versus delivered format

MP3 and WAV requests are always admitted. Featherless can rewrap raw PCM and WAV without a lossy codec transcode. When a model emits another fixed container and no converter exists, the API returns that native container with an honest Content-Type. In JSON mode, the format field identifies the returned bytes. SSE currently has no format field, so the delivered container cannot be read from the stream itself. Do not use SSE for a model whose delivered container may differ from the requested one unless the client has reliable out-of-band format knowledge; bulk Content-Type or JSON format is safer.

Cancellation and timeout behavior

Disconnecting aborts the Featherless request and triggers best-effort cancellation. The platform currently applies a 55-second deadline while waiting for audio to become available; that deadline does not cover the complete subsequent body download. Do not assume that inference stops immediately after a client disconnect.

Last edited: Aug 26, 2026