ElevenLabs
Speech-to-text with ElevenLabsSTTService (segmented uploads) and ElevenLabsRealtimeSTTService (WebSocket streaming).
Overview
ElevenLabs Scribe transcribes speech. pipedog ports both of Pipecat's ElevenLabs speech-to-text services:
ElevenLabsSTTService: uploads each speech segment, cut by the pipeline's VAD, to ElevenLabs'/v1/speech-to-textendpoint and pushes the transcript it gets back.ElevenLabsRealtimeSTTService: streams audio over ElevenLabs' realtime WebSocket API, with partial and committed transcripts. Segments are committed by the pipeline's VAD or by ElevenLabs' own.
Upstream page
Pipecat's documentation for the same services.
Source
packages/elevenlabs/src/stt.ts.
ElevenLabs docs
Scribe models, languages and the realtime API.
Installation
bun add @pipedog/elevenlabsimport {
CommitStrategy,
ElevenLabsRealtimeSTTService,
ElevenLabsSTTService,
} from "@pipedog/elevenlabs/stt";Prerequisites
- An ElevenLabs account and an API key from the ElevenLabs dashboard.
- Pass the key as
apiKey. The examples read it fromELEVENLABS_API_KEY.
ElevenLabsSTTService
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new ElevenLabsSTTService.Settings({...}). Any
setting you leave out keeps the default below. null means the field is not sent, so ElevenLabs
uses its own default.
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "scribe_v2" | Scribe model. |
language | Language | null | Language.EN | Transcription language. null lets ElevenLabs detect it. |
tagAudioEvents | boolean | null | null | Include audio events such as (laughter) in the transcript. |
keyterms | string[] | null | null | Terms to bias transcription towards. |
noVerbatim | boolean | null | null | Drop filler words, false starts and non-speech sounds. scribe_v2 only. |
Usage
import { STTUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { Language } from "@pipedog/core/transcriptions/language";
import {
CommitStrategy,
ElevenLabsRealtimeSTTService,
ElevenLabsSTTService,
} from "@pipedog/elevenlabs/stt";
const stt = new ElevenLabsSTTService({
apiKey: process.env.ELEVENLABS_API_KEY ?? "",
settings: new ElevenLabsSTTService.Settings({
model: "scribe_v2",
language: Language.EN,
keyterms: ["pipedog", "Pipecat"],
}),
});Notes
- A segmented service: it needs VAD in the pipeline (on the user aggregator). Audio between the VAD's start and stop is sent as one WAV upload, padded with a little trailing silence.
- Transcription runs in the background, so audio keeps flowing while a segment is transcribed.
- Emits one
TranscriptionFrameper segment, with the language code ElevenLabs returns. There are no interim results.
ElevenLabsRealtimeSTTService
Configuration
Prop
Type
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "scribe_v2_realtime" | Scribe realtime model. |
language | Language | null | null (auto-detect) | Transcription language. |
keyterms | string[] | null | null | Terms to bias transcription towards. |
noVerbatim | boolean | null | null | Drop filler words, false starts and non-speech sounds. |
vadSilenceThresholdSecs | number | null | null | Silence before ElevenLabs' VAD commits, 0.3 to 3.0 s. CommitStrategy.VAD only. |
vadThreshold | number | null | null | VAD sensitivity, 0.1 to 0.9 (lower is more sensitive). CommitStrategy.VAD only. |
minSpeechDurationMs | number | null | null | Minimum speech duration, 50 to 2000 ms. CommitStrategy.VAD only. |
minSilenceDurationMs | number | null | null | Minimum silence duration, 50 to 2000 ms. CommitStrategy.VAD only. |
filterBackgroundAudio | boolean | null | null | Filter background audio before transcription. |
Usage
const realtime = new ElevenLabsRealtimeSTTService({
apiKey: process.env.ELEVENLABS_API_KEY ?? "",
settings: new ElevenLabsRealtimeSTTService.Settings({
language: Language.EN,
keyterms: ["pipedog"],
}),
});With ElevenLabs' VAD committing segments:
// Let ElevenLabs decide when a segment ends instead of the pipeline's VAD.
const serverVad = new ElevenLabsRealtimeSTTService({
apiKey: process.env.ELEVENLABS_API_KEY ?? "",
commitStrategy: CommitStrategy.VAD,
includeTimestamps: true,
settings: new ElevenLabsRealtimeSTTService.Settings({
vadSilenceThresholdSecs: 0.8,
vadThreshold: 0.4,
}),
});Change settings mid-call with an STTUpdateSettingsFrame. The service reconnects with the new
settings:
// Queue it on the worker, or push it from a processor upstream of the service.
const update = new STTUpdateSettingsFrame({
delta: new ElevenLabsRealtimeSTTService.Settings({ language: Language.ES }),
});Event handlers
Inherited from STTService:
| Event | Description |
|---|---|
on_connected | Connected to ElevenLabs. |
on_disconnected | Disconnected from ElevenLabs. |
on_connection_error | The connection failed. |
Notes
- Emits
InterimTranscriptionFramefor partial transcripts andTranscriptionFramefor committed ones. - With
CommitStrategy.MANUAL(the default), aVADUserStoppedSpeakingFramemakes the service send a commit, so the pipeline's VAD decides where segments end. WithCommitStrategy.VAD, ElevenLabs decides, using thevad*andmin*DurationMssettings. - The audio format is chosen from the sample rate: 8000, 16000, 22050, 24000, 44100 or 48000 Hz.
Any other rate falls back to
pcm_16000with a warning. - The service sends silence as a keepalive after 10 s without audio (checked every 5 s).
- Error messages from ElevenLabs (authentication, quota, rate limit, throttled commits, session time limit and others) are pushed as errors.
baseUrlis a bare host and defaults to"api.elevenlabs.io".
Runtime notes
| 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/elevenlabs/package.json: ✅ means the compat smoke test passes on that runtime.
ElevenLabsSTTServiceuploads withfetchandFormData. Upstream's requiredaiohttp_sessionis replaced by an optionalfetchoption (the globalfetchby default).ElevenLabsRealtimeSTTServiceuses the globalWebSocketand sends the key as anxi-api-keyheader on the handshake. That works on Bun, Node and Deno, and on Cloudflare Workers, where the socket is opened throughfetch.- Not ported: the deprecated
InputParamsclasses and themodel/paramsconstructor options. Usesettings.
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).