Streaming TTS in Python with Speechify

How to stream audio from the Speechify TTS API in Python with plain HTTP and requests. Covers chunked streaming to disk and piping audio to a player without waiting for the full payload.

Updated 3 min read

The Node.js TTS post covers the same patterns in TypeScript. This post is the Python companion.

Why streaming matters

Python TTS is going to be mostly server-side. Batch jobs, content pipelines, document-to-audio converters. Waiting for the full payload before writing is fine when you’re generating a few seconds of audio at a time, but once you are synthesizing anything substantial, the length and payload size compounds. Now, the streaming endpoints take up to 20,000 spoken characters per request (SSML tags do not count; the non-streaming speech endpoint takes 2,000), which is huge. We’re not slow, but at that length non-streaming becomes a process block until the entire clip is generated and downloaded by your service.

Streaming starts to return audio data the moment it is generated, and you can write it to disk as quickly as it arrives. Time-to-first-byte can be dramatically quicker, memory pressure drops, and you can start playing or forwarding audio before synthesis is complete.

Install and setup

The API is plain HTTPS, so all you need is an HTTP client. This post uses requests, plus python-dotenv to load the key:

pip install requests python-dotenv

Or with uv:

uv add requests python-dotenv

Create a .env file with your key:

SPEECHIFY_API_KEY=your_key_here

Get a key from the Speechify console.

Streaming to disk with requests

POST /v1/audio/stream returns raw audio bytes as they are synthesized. Send the request with stream=True, iterate the body, and write each chunk to a file.

import os
import requests
from dotenv import load_dotenv

def main() -> None:
    load_dotenv()
    token = os.environ.get("SPEECHIFY_API_KEY")
    if not token:
        raise SystemExit("Set SPEECHIFY_API_KEY (copy .env.example to .env).")

    with requests.post(
        "https://api.speechify.ai/v1/audio/stream",
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
            "Accept": "audio/mpeg",
        },
        json={
            "input": (
                "Streaming lets you start playing audio before the whole clip is ready. "
                "This sentence is being synthesized and written to disk chunk by chunk."
            ),
            "voice_id": "geffen_32",
            "model": "simba-3.2",
        },
        stream=True,
        timeout=60,
    ) as resp:
        resp.raise_for_status()
        out_file = "output.mp3"
        with open(out_file, "wb") as f:
            for chunk in resp.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
        print(f"Streamed audio to {out_file}")

if __name__ == "__main__":
    main()

stream=True on the requests.post call tells requests not to buffer the response body. iter_content(chunk_size=8192) gives you 8 KB chunks as they arrive. The if chunk guard skips keepalive bytes. The Accept header picks the container: audio/mpeg, audio/ogg, audio/aac, or audio/pcm (returns audio/L16, 24 kHz mono).

Pipe to ffplay for live playback

During development you often just want to hear the output immediately without writing a file. Pipe the stream directly to ffplay:

import os
import subprocess
import requests
from dotenv import load_dotenv

def main() -> None:
    load_dotenv()
    token = os.environ.get("SPEECHIFY_API_KEY")
    if not token:
        raise SystemExit("Set SPEECHIFY_API_KEY")

    player = subprocess.Popen(
        ["ffplay", "-nodisp", "-autoexit", "-i", "pipe:0"],
        stdin=subprocess.PIPE,
        stderr=subprocess.DEVNULL,
    )

    try:
        with requests.post(
            "https://api.speechify.ai/v1/audio/stream",
            headers={
                "Authorization": f"Bearer {token}",
                "Content-Type": "application/json",
                "Accept": "audio/mpeg",
            },
            json={"input": "Hello from the pipe.", "voice_id": "geffen_32", "model": "simba-3.2"},
            stream=True,
            timeout=60,
        ) as resp:
            resp.raise_for_status()
            for chunk in resp.iter_content(chunk_size=8192):
                if chunk:
                    player.stdin.write(chunk)
    finally:
        player.stdin.close()
        player.wait()

if __name__ == "__main__":
    main()

ffplay reads from pipe:0 (stdin), starts playing as soon as it has enough data to decode the first frame, and exits when the stream closes. You need ffmpeg installed (brew install ffmpeg or apt install ffmpeg). This is a development tool, not a production pattern.

What’s next

The Speechify TTS docs cover the full parameter surface: voice selection, SSML, and word-level timestamps.

This post is narrated by Harper on Simba 3.2 through our text to speech API. How the player works: add read-aloud to your docs and turn speech marks into captions.

Privacy preferences

Choose what we may store on this device. You can change this at any time from the footer.

Strictly necessary

Sign-in, security, load balancing, and remembering your privacy choices. These cannot be switched off.

Always on

Analytics

How the site is used in aggregate - which pages get read, where people get stuck - so we can improve it.

Marketing

Measures which campaigns bring people here, and lets us show relevant ads on other platforms.