Speechify
Text-to-speech with Speechify: SpeechifyHttpTTSService streams audio and word timestamps over Server-Sent Events.
Overview
SpeechifyHttpTTSService posts each sentence to Speechify's /v1/audio/stream/with-timestamps endpoint and reads the Server-Sent Events it answers with: PCM audio chunks and word-level speech marks. The marks become word timestamps, so the bot's TTSTextFrames follow the audio.
Upstream page
Pipecat's documentation for the same service.
Source
packages/speechify/src/tts.ts.
Speechify docs
The stream-with-timestamps API reference.
Installation
bun add @pipedog/speechifyimport { SpeechifyHttpTTSService } from "@pipedog/speechify/tts";Prerequisites
- A Speechify API account and an API key.
- Pass the key as
apiKey. The examples read it fromSPEECHIFY_API_KEY.
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new SpeechifyHttpTTSService.Settings({...})
(SpeechifyTTSSettings). Any setting you leave out keeps the default below.
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "simba-3.5" | Speech model, such as simba-3.5 or simba-3.0 (multilingual). |
voice | string | "geffen_32" | Voice id. |
language | Language | null | null | Language, sent as a tag such as en-US. |
loudnessNormalization | boolean | null | null | Normalize the audio's loudness. Adds latency. |
textNormalization | boolean | null | null | Spell out numbers, dates and similar tokens. Adds latency. |
safetyIdentifier | string | null | null | A stable, opaque id for the end user (1 to 64 characters), so Speechify can limit one user instead of the workspace. Never an email address. |
pronunciations | Dict[] | null | null | How specific words are spoken (up to 200), by IPA phones or a spoken alias. Applied on simba-3.5 in the languages Speechify lists for pronunciations, which excludes English. |
Usage
import { TTSUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { SpeechifyHttpTTSService } from "@pipedog/speechify/tts";
const tts = new SpeechifyHttpTTSService({
apiKey: process.env.SPEECHIFY_API_KEY ?? "",
settings: new SpeechifyHttpTTSService.Settings({
model: "simba-3.5",
voice: "geffen_32",
textNormalization: true,
}),
});Change settings mid-call with a TTSUpdateSettingsFrame:
// Queue it on the worker, or push it from a processor upstream of the service.
const update = new TTSUpdateSettingsFrame({
delta: new SpeechifyHttpTTSService.Settings({ voice: "another-voice-id" }),
});Notes
- The sample rate must be one of Speechify's PCM rates (8000, 16000, 22050, 24000, 44100 or 48000). For any other pipeline rate the service asks for 24000 Hz, tags its audio frames with that rate, and the output transport resamples.
- Requests pin Speechify's API version (
Speechify-Version: 2026-09-30), and sendSpeechify-Caller: pipecatwith the pipedog version. - Speechify marks the pieces of a hyphenated word separately. The service joins marks that touch in the text, so
text-to-speechis one word timestamp. - A mid-stream
speech.errorevent is reported as an error frame. A non-200 response is too.
Runtime notes
- It uses the global
fetchand reads the response as a stream, so it works on Bun, Node, Deno and Cloudflare Workers. Passfetchto use another one (upstream takes an aiohttp session). - Tested against a local server that replays Speechify's event format, as upstream's tests do for the parsing, not against Speechify itself.
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing. |
| Node.js | ✅ yes | Nothing. |
| Deno | ✅ yes | Nothing. |
| Cloudflare Workers | ✅ yes | Nothing. |
Generated from pipecat.runtimes and pipecat.compat in packages/speechify/package.json: ✅ means the compat smoke test passes on that runtime.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).