SAGEA LogoDocs & API
CookbooksVoice Cookbooks

Realtime Streaming with Sonus

Stream speech with sub-200ms first byte using POST https://api.sagea.space/v1/audio/stream.

  • Open a chunked stream and play audio while it generates
  • Measure time to first byte against the 200ms target
  • Pipe chunks to a player for smooth realtime playback

Time to complete: ~15 minutes

Prerequisites

  • Python 3.9+ with pip install requests.
  • A media player (ffplay, mpv, or aplay) for chunked MP3 playback.
  • A SAGEA API key exported as SAGEA_API_KEY.
  • Read Synthesize speech with Sonus for request basics.
  • Skim the Sonus TTS model card for streaming formats and sample rates.

Step 1: Open a chunked stream

Request POST https://api.sagea.space/v1/audio/stream with the synthesis body. Audio arrives in chunks so playback starts early.

curl -N -X POST https://api.sagea.space/v1/audio/stream \
  -H "Authorization: Bearer $SAGEA_API_KEY" -H "Content-Type: application/json" \
  -d '{"model": "sonus-tts", "input": "Welcome back. I can start speaking before I finish thinking.", "voice": "nova", "response_format": "mp3", "emotion": "friendly", "speed": 1.0}' \
  --output stream.mp3

The -N flag disables curl buffering so chunks hit disk immediately.

Step 2: Measure first-byte timing

Time dispatch to first audio byte. Sonus targets sub-200ms first byte on this endpoint.

time curl -N -X POST https://api.sagea.space/v1/audio/stream \
  -H "Authorization: Bearer $SAGEA_API_KEY" -H "Content-Type: application/json" \
  -d '{"model": "sonus-tts", "input": "Counting one two three.", "voice": "nova", "response_format": "mp3"}' \
  --output timing.mp3

If first byte exceeds 200ms, shorten input or prefer mp3 over flac.

Step 3: Pipe chunks to a realtime player

Write chunks to a player pipe as they arrive instead of waiting for the file:

# realtime_player.py
import os, subprocess, requests
player = subprocess.Popen(["mpv", "--no-cache", "--no-terminal", "-"], stdin=subprocess.PIPE)
r = requests.post("https://api.sagea.space/v1/audio/stream", headers={"Authorization": f"Bearer {os.environ['SAGEA_API_KEY']}"}, json={"model": "sonus-tts", "input": "This audio plays while it is still being generated.", "voice": "nova", "response_format": "mp3", "emotion": "excited", "speed": 1.0}, stream=True, timeout=60)
r.raise_for_status()
for chunk in r.iter_content(chunk_size=4096):
    if chunk and player.stdin:
        player.stdin.write(chunk)
        player.stdin.flush()
if player.stdin:
    player.stdin.close()
player.wait()

Swap mpv for ffplay -nodisp -autoexit - if needed. Keep 4KB chunks to balance latency and overhead.

Verify

Playback should start almost instantly with no gaps. If it stalls, check the error table below.

ErrorCauseFix
401 UnauthorizedAPI key missing or invalidRe-export SAGEA_API_KEY and retry
400 text_too_longInput exceeds 4096 charsStream sentence by sentence under 4096 chars each
422 voice_not_foundUnknown voice nameUse nova or a valid voice_id starting with vox_
429 rate_limit_exceededOver 50 req per min on Starter, 500 on ProThrottle streams and add backoff

What's next

On this page