> This page is for Build.

> Append .md to any page URL for clean Markdown. Index: https://docs.speechify.ai/llms.txt.
>
> Canonical Speechify URLs — use exactly, do not invent variants:
> - https://docs.speechify.ai — this site (API reference, SDKs, quickstarts)
> - https://speechify.ai — marketing + product site
> - https://platform.speechify.ai — customer dashboard, signup, API keys, billing
> - https://api.speechify.ai — API base URL
> - https://github.com/Speechify-AI: GitHub org for the API (cookbook, demos, CLI). `github.com/speechify` does not exist.
> - https://status.speechify.ai — status + incidents
> - https://speechify.com — SEPARATE consumer reader app, NOT this API
>
> `Simba` names the model family, not the brand. Model ids: `simba-3.2` (English, recommended) and `simba-3.0` (English, German, Spanish, French, Italian and Portuguese; the default). `simba-english` and `simba-multilingual` are retired: a new workspace that sends either gets `400 model_retired`. `SimbaVoice` / `simbavoice.ai` are retired.
>
> Ask, don't scrape. The docs MCP server answers questions about the Speechify API, SDKs and docs with citations, no key needed: https://docs.speechify.ai/_mcp/server (Streamable HTTP, tool `searchDocs`). Setup: https://docs.speechify.ai/build/guides/get-started/connect-mcp

# Pipecat

> Use Pipecat's built-in SpeechifyHttpTTSService (pipecat-ai 1.8.0 and later) to add Speechify text-to-speech, with word-level timestamps, to a Pipecat voice agent.

## Overview

Pipecat ships Speechify TTS in its core package as `SpeechifyHttpTTSService`, first released in `pipecat-ai` 1.8.0. It is the TTS stage of a Pipecat pipeline: for each sentence the LLM writes, it calls [`POST /v1/audio/stream/with-timestamps`](/build/guides/text-to-speech/speech-marks), reads the Server-Sent Events stream, and pushes PCM audio frames and word timestamps downstream as they arrive.

Pipecat maintains the service. Its [Speechify reference](https://docs.pipecat.ai/api-reference/server/services/tts/speechify) lists every argument.

## Prerequisites

* Speechify API key ([platform.speechify.ai/api-keys](https://platform.speechify.ai/api-keys))
* Python 3.11 or later
* For the full agent: a Deepgram key for STT and an OpenAI key for the LLM (this guide's stack, swap freely)

## Install

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install "pipecat-ai[speechify,deepgram,openai,silero,webrtc,runner]>=1.8.0" python-dotenv
```

`pipecat-ai` versions before 1.8.0 have no Speechify service. For the TTS smoke test alone, `pip install "pipecat-ai[speechify]>=1.8.0"` is enough.

## Configure

Put credentials in `.env`:

```bash
SPEECHIFY_API_KEY=your_speechify_api_key
DEEPGRAM_API_KEY=your_deepgram_api_key
OPENAI_API_KEY=your_openai_api_key
```

The service does not read the environment itself. Pass the key as `api_key=`.

## Create the service

Inside your bot's async entry point:

```python
import os

import aiohttp

from pipecat.services.speechify.tts import SpeechifyHttpTTSService

async with aiohttp.ClientSession() as session:
    tts = SpeechifyHttpTTSService(
        api_key=os.environ["SPEECHIFY_API_KEY"],
        aiohttp_session=session,
        settings=SpeechifyHttpTTSService.Settings(voice="geffen_32", model="simba-3.2"),
    )
```

| Argument                                                         | Default                    | Notes                                                                                                                                                                             |
| ---------------------------------------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api_key`                                                        | Required                   | Your Speechify API key.                                                                                                                                                           |
| `aiohttp_session`                                                | Required                   | An `aiohttp.ClientSession` you create and close. Keep it open for as long as the pipeline runs.                                                                                   |
| `base_url`                                                       | `https://api.speechify.ai` | Leave it unset. The service appends `/v1/audio/stream/with-timestamps`, so a value ending in `/v1` requests `/v1/v1/...` and fails with `404`.                                    |
| `sample_rate`                                                    | The pipeline's output rate | Sent as `output_format: pcm_<rate>`. Supported rates are 8000, 16000, 22050, 24000, 44100 and 48000 Hz; any other rate falls back to 24000 Hz and the output transport resamples. |
| `settings.voice`                                                 | `geffen_32`                | Any voice whose `models` list includes `settings.model`.                                                                                                                          |
| `settings.model`                                                 | `simba-3.2`                | `simba-3.2` for English, `simba-3.0` for other languages.                                                                                                                         |
| `settings.language`                                              | Not sent                   | A Pipecat `Language`: `DE`, `EN`, `ES`, `FR`, `IT` and `PT` map to `de-DE`, `en-US`, `es-ES`, `fr-FR`, `it-IT` and `pt-BR`.                                                       |
| `settings.loudness_normalization`, `settings.text_normalization` | Not sent                   | Each adds latency.                                                                                                                                                                |

Change any setting mid-conversation by queueing a `TTSUpdateSettingsFrame(delta=SpeechifyHttpTTSService.Settings(...))`.

## Pair `voice` and `model` by language

| Language                                     | `model`     | `voice`                                                                                                      |
| -------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
| English                                      | `simba-3.2` | Any English voice: the `*_32` roster (`geffen_32`, `dominic_32`, `harper_32`, ...) or your own cloned voices |
| German, Spanish, French, Italian, Portuguese | `simba-3.0` | A voice in that language, with the matching `settings.language`                                              |

```python
from pipecat.transcriptions.language import Language

settings = SpeechifyHttpTTSService.Settings(voice="anton", model="simba-3.0", language=Language.DE)
```

`simba-3.2` is English only and returns `400` for a non-English voice. Each voice's `models` list in [`GET /v1/voices`](/build/api-reference/v1/voices/get) is the authority. Store `voice` and `model` together in config so a later swap cannot mismatch them.

> **Note**
>
> Use `simba-3.2` or `simba-3.0` with this service. Older model ids are retired from API version `2026-09-21` and return `400` `model_retired`. See [Models](/build/guides/concepts/models).

## Word timestamps

Each `speech.chunk` event carries base64 PCM, the speech marks that became final with it, or both: marks lag their audio slightly, and the last chunk is often marks-only. Mark times are absolute milliseconds from the start of the request's audio, not offsets into the chunk they arrive on. The service joins the marks into whole words and pushes each one as a `TTSTextFrame` whose presentation timestamp is the moment that word starts playing.

Pipecat uses those frames to record what the bot actually said. When the user interrupts, the assistant's turn in the LLM context keeps only the words that were spoken, not the whole reply the LLM wrote.

## Verify the TTS stage

This script runs a two-stage pipeline, the Speechify service and a processor that captures its output, so it needs only your Speechify key:

```python
import asyncio
import os
import wave

import aiohttp

from pipecat.frames.frames import EndFrame, TTSAudioRawFrame, TTSSpeakFrame, TTSTextFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.worker import PipelineWorker
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
from pipecat.services.speechify.tts import SpeechifyHttpTTSService
from pipecat.workers.runner import WorkerRunner


class Capture(FrameProcessor):
    def __init__(self):
        super().__init__()
        self.audio = bytearray()
        self.audio_frames = 0
        self.sample_rate = 0
        self.words = []

    async def process_frame(self, frame, direction: FrameDirection):
        await super().process_frame(frame, direction)
        if isinstance(frame, TTSAudioRawFrame):
            self.audio += frame.audio
            self.audio_frames += 1
            self.sample_rate = frame.sample_rate
        elif isinstance(frame, TTSTextFrame):
            self.words.append((frame.text, frame.pts))
        await self.push_frame(frame, direction)


async def main():
    async with aiohttp.ClientSession() as session:
        tts = SpeechifyHttpTTSService(
            api_key=os.environ["SPEECHIFY_API_KEY"],
            aiohttp_session=session,
            settings=SpeechifyHttpTTSService.Settings(voice="geffen_32", model="simba-3.2"),
        )
        capture = Capture()
        worker = PipelineWorker(Pipeline([tts, capture]))
        await worker.queue_frames([TTSSpeakFrame("Hello from Speechify on Pipecat."), EndFrame()])

        runner = WorkerRunner(handle_sigint=False)
        await runner.add_workers(worker)
        await runner.run()

    with wave.open("speechify-pipecat.wav", "wb") as wav:
        wav.setnchannels(1)
        wav.setsampwidth(2)
        wav.setframerate(capture.sample_rate)
        wav.writeframes(capture.audio)

    seconds = len(capture.audio) / 2 / capture.sample_rate
    print(f"{capture.audio_frames} audio frames, {seconds:.2f}s at {capture.sample_rate} Hz")
    first = capture.words[0][1]
    for word, pts in capture.words:
        print(f"{(pts - first) / 1e9:5.2f}s  {word}")


asyncio.run(main())
```

It writes `speechify-pipecat.wav` and prints the audio frames and word timestamps it received. Frame counts and times vary from run to run:

```text
12 audio frames, 2.77s at 24000 Hz
 0.00s  Hello
 0.46s  from
 0.63s  Speechify
 1.74s  on
 1.96s  Pipecat.
```

## Run a full agent

`bot.py` wires Speechify into a Deepgram and OpenAI pipeline and serves it over Pipecat's local WebRTC transport:

```python
import os

import aiohttp
from dotenv import load_dotenv

from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import LLMRunFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.worker import PipelineParams, PipelineWorker
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import (
    LLMContextAggregatorPair,
    LLMUserAggregatorParams,
)
from pipecat.runner.types import RunnerArguments
from pipecat.runner.utils import create_transport
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.services.speechify.tts import SpeechifyHttpTTSService
from pipecat.transports.base_transport import TransportParams
from pipecat.workers.runner import WorkerRunner

load_dotenv(override=True)


async def bot(runner_args: RunnerArguments):
    transport = await create_transport(
        runner_args,
        {"webrtc": lambda: TransportParams(audio_in_enabled=True, audio_out_enabled=True)},
    )

    async with aiohttp.ClientSession() as session:
        stt = DeepgramSTTService(api_key=os.environ["DEEPGRAM_API_KEY"])
        llm = OpenAILLMService(
            api_key=os.environ["OPENAI_API_KEY"],
            settings=OpenAILLMService.Settings(
                system_instruction=(
                    "You are a helpful voice assistant. Your replies are spoken aloud, "
                    "so keep them short and avoid lists, markdown and emojis."
                ),
            ),
        )
        tts = SpeechifyHttpTTSService(
            api_key=os.environ["SPEECHIFY_API_KEY"],
            aiohttp_session=session,
            settings=SpeechifyHttpTTSService.Settings(voice="geffen_32", model="simba-3.2"),
        )

        context = LLMContext()
        user_aggregator, assistant_aggregator = LLMContextAggregatorPair(
            context,
            user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()),
        )

        pipeline = Pipeline(
            [
                transport.input(),
                stt,
                user_aggregator,
                llm,
                tts,
                transport.output(),
                assistant_aggregator,
            ]
        )

        worker = PipelineWorker(pipeline, params=PipelineParams(enable_metrics=True))
        runner = WorkerRunner(handle_sigint=runner_args.handle_sigint)

        @transport.event_handler("on_client_connected")
        async def on_client_connected(transport, client):
            context.add_message({"role": "developer", "content": "Greet the user in one short sentence."})
            await worker.queue_frames([LLMRunFrame()])

        @transport.event_handler("on_client_disconnected")
        async def on_client_disconnected(transport, client):
            await runner.cancel()

        await runner.add_workers(worker)
        await runner.run()


if __name__ == "__main__":
    from pipecat.runner.run import main

    main()
```

Pipeline order matters: `transport.input()` receives the user's audio, Deepgram transcribes it, the user aggregator adds the transcript to the LLM context, the LLM writes the reply, Speechify speaks it, and `transport.output()` plays it back. The assistant aggregator sits last so it records only what reached the transport.

Run it and open [http://localhost:7860](http://localhost:7860) to talk to the agent in the browser:

```bash
python bot.py -t webrtc
```

The session lives inside `async with aiohttp.ClientSession()`, so it stays open for the whole conversation and closes when the runner ends.

## Sample rate and your API version

The service requests audio at the pipeline's output rate, 24 kHz unless your transport or `PipelineParams(audio_out_sample_rate=...)` sets another. It sends no `Speechify-Version` header, so your workspace's pinned [API version](/build/guides/concepts/api-versioning) decides how the request is served.

> **Warning**
>
> On a workspace pinned before `2026-09-30`, `pcm_16000` returns 24 kHz audio, which a 16 kHz pipeline plays 1.5x slow and pitched down. If your pipeline runs at 16 kHz, move the pin to `2026-09-30` or later, or pass `sample_rate=24000` to the service so the output transport resamples the audio. See the [`pcm_16000` changelog entry](/build/changelog/2026/9/30).

## Troubleshooting

#### \`missing 1 required keyword-only argument: 'aiohttp\_session'\`

The service needs an `aiohttp.ClientSession`. Create one with `async with aiohttp.ClientSession() as session:` and pass it as `aiohttp_session=session`.

#### \`cannot import name 'SpeechifyTTSService'\` or no module \`pipecat.services.speechify\`

The class is `SpeechifyHttpTTSService`, and it first shipped in `pipecat-ai` 1.8.0. Upgrade with `pip install -U "pipecat-ai[speechify]>=1.8.0"`.

#### \`Speechify API error\` with code \`not\_found\`

`base_url` ends in `/v1`. Remove the argument; the default is `https://api.speechify.ai`.

#### \`400\` when the bot speaks

The voice and model do not pair. `simba-3.2` takes English voices only; use `simba-3.0` with a voice in another language. The voice's `models` list in `GET /v1/voices` names every model it pairs with.

## Resources

#### [Pipecat Speechify reference](https://docs.pipecat.ai/api-reference/server/services/tts/speechify)

Every `SpeechifyHttpTTSService` argument and setting, maintained by Pipecat.

#### [Pipecat example](https://github.com/pipecat-ai/pipecat/blob/main/examples/voice/voice-speechify-http.py)

Pipecat's own Speechify bot, with Daily and Twilio transports as well as WebRTC.

#### [Speech marks](/build/guides/text-to-speech/speech-marks)

The `/v1/audio/stream/with-timestamps` event format the service reads.

#### [Voice catalogue](/build/api-reference/v1/voices/get)

`GET /v1/voices`: each voice's `models` list names the models it pairs with.