Quickdial API
Text-to-speech and speech-to-text over REST and WebSocket. Try every endpoint live below — synthesize a voice and download the audio, or upload a clip and get a transcript.
Overview
The Quickdial API has two directions, each on REST and WebSocket. REST is a single request/response; WebSocket streams audio and text continuously for the lowest latency. All examples below are runnable against this server.
Authentication
Authenticate with a Bearer API key (create one in the dashboard). Paste it once here — the runnable examples on this page will use it. Leave it blank to use the free, rate-limited public demo instead.
# Every REST call takes an Authorization header
curl https://api.quickdial.ai/v1/voices \
-H "Authorization: Bearer qdl_live_your_key"TTS Text-to-Speech
Send text and a voice, get audio back. Choose opus for the cheapest streaming, or wav for uncompressed PCM.
curl https://api.quickdial.ai/v1/tts \
-H "Authorization: Bearer qdl_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "text": "Hello world", "voice": "alba", "format": "wav" }' \
--output hello.wavVoices
List every available voice with its language and gender. This endpoint is public — no key required.
curl https://api.quickdial.ai/v1/voices \
-H "Authorization: Bearer qdl_live_your_key"STT Speech-to-Text
Upload an audio file and get an accurate transcript with word-level timestamps, powered by whisper.cpp.
curl https://api.quickdial.ai/v1/stt \
-H "Authorization: Bearer qdl_live_your_key" \
-F audio=@speech.wavWebSocket streaming
For the lowest latency, keep a socket open so audio and text flow continuously — no request/response round-trip per chunk. Same voices and models as REST, relayed byte-for-byte. Streaming isn't runnable from this page, but the protocol and full code are below.
| Direction | Text → Speech | Speech → Text |
|---|---|---|
| You send | One JSON message (text + voice) | A stream of audio frames, then eos |
| You receive | Binary PCM audio frames | A JSON transcript |
| Audio format | 16-bit PCM · mono · 24 kHz | 16-bit PCM · mono · 16 kHz |
Authentication
Browsers can't set headers on a WebSocket handshake, so pass your key as a query parameter. Non-browser clients may send an Authorization header instead.
// Browser — key in the query string
const ws = new WebSocket("wss://api.quickdial.ai/v1/tts/stream?key=qdl_live_your_key");
// Node / server — Authorization header on the handshake
const ws = new WebSocket("wss://api.quickdial.ai/v1/tts/stream", {
headers: { Authorization: "Bearer qdl_live_your_key" },
});TTS Text → Speech streaming
Open the socket, send one JSON message, and receive PCM audio frames as they're synthesized — start playing before the sentence finishes.
const KEY = "qdl_live_your_key";
const ctx = new AudioContext({ sampleRate: 24000 });
let playhead = ctx.currentTime;
const ws = new WebSocket(`wss://api.quickdial.ai/v1/tts/stream?key=${KEY}`);
ws.binaryType = "arraybuffer";
ws.onopen = () =>
ws.send(JSON.stringify({ text: "Hello from Quickdial, streamed in real time.", voice: "jane" }));
ws.onmessage = (e) => {
if (typeof e.data === "string") { // control frames are text
const msg = JSON.parse(e.data);
if (msg.type === "error") console.error(msg.message);
return;
}
const pcm = new Int16Array(e.data); // 16-bit PCM mono @ 24 kHz → Web Audio
const buf = ctx.createBuffer(1, pcm.length, 24000);
const ch = buf.getChannelData(0);
for (let i = 0; i < pcm.length; i++) ch[i] = pcm[i] / 32768;
const src = ctx.createBufferSource();
src.buffer = buf; src.connect(ctx.destination);
playhead = Math.max(playhead, ctx.currentTime);
src.start(playhead);
playhead += buf.duration;
};STT Speech → Text streaming
Open the socket, (optionally) set params, stream 16 kHz PCM frames as you capture them, then send eos to receive the transcript.
const KEY = "qdl_live_your_key";
const ws = new WebSocket(`wss://api.quickdial.ai/v1/stt/stream?key=${KEY}`);
ws.onopen = async () => {
ws.send(JSON.stringify({ params: { language: "en" } })); // optional config
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const ac = new AudioContext({ sampleRate: 16000 });
const src = ac.createMediaStreamSource(stream);
const node = ac.createScriptProcessor(4096, 1, 1);
src.connect(node); node.connect(ac.destination);
node.onaudioprocess = (e) => {
const f32 = e.inputBuffer.getChannelData(0);
const pcm = new Int16Array(f32.length); // float32 → 16-bit PCM
for (let i = 0; i < f32.length; i++) pcm[i] = Math.max(-1, Math.min(1, f32[i])) * 32767;
if (ws.readyState === 1) ws.send(pcm.buffer);
};
setTimeout(() => { node.disconnect(); ws.send(JSON.stringify({ type: "eos" })); }, 5000);
};
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "transcript") console.log(msg.text, msg.segments);
};Message reference
Server → client
| Type | Sent by | Payload |
|---|---|---|
start | TTS & STT | { type, requestId, voice?, sample_rate } — session opened |
| binary | TTS | Raw 16-bit little-endian PCM, mono, 24 kHz — one or more frames |
ready | STT | Acknowledges a {params} config message |
transcript | STT | { type, text, language, duration_seconds, segments:[{start,end,text}] } |
end | TTS | { type, requestId } — synthesis complete |
error | both | { type, message } |
Client → server
| Message | Endpoint | Meaning |
|---|---|---|
{ text, voice, params? } | TTS | Synthesize this text (one per socket message) |
{ params } | STT | Optional config before audio (language, translate, …) |
| binary | STT | 16-bit PCM mono @ 16 kHz audio frame |
{ type: "eos" } | STT | End of utterance → returns a transcript |
Integrations LiveKit Pipecat
Official plugins let you drop Quickdial STT & TTS into the popular voice-agent frameworks. Set QUICKDIAL_API_KEY in your environment, then swap in the components below.
LiveKit Agents Python v0.1.4 Node v0.1.4
# pip install livekit-plugins-quickdial (v0.1.4)
from livekit.agents import AgentSession
from livekit.plugins import quickdial, silero
session = AgentSession(
stt=quickdial.STT(language="en"), # POST /v1/stt (whisper.cpp)
tts=quickdial.TTS(voice="alba"), # POST /v1/tts, 24 kHz
vad=silero.VAD.load(),
)
# export QUICKDIAL_API_KEY=qtts_live_...// npm i @quickdial-ai/livekit-plugins-quickdial (v0.1.4)
import * as quickdial from '@quickdial-ai/livekit-plugins-quickdial';
const session = new voice.AgentSession({
stt: new quickdial.STT({ language: 'en' }),
tts: new quickdial.TTS({ voice: 'alba' }),
});
// QUICKDIAL_API_KEY=qtts_live_...Pipecat Python v0.1.0
# pip install pipecat-quickdial (v0.1.0)
from pipecat_quickdial import QuickdialSTTService, QuickdialTTSService
stt = QuickdialSTTService(language="en") # QUICKDIAL_API_KEY from env
tts = QuickdialTTSService(voice="alba")
# add stt / tts to your Pipecat Pipeline([...])/v1/tts, /v1/stt) — see the sections above for parameters and voices.Errors & tips
- 401 / handshake close 4401 — missing or invalid API key. Check the header or
?key=param. - 402 — free credits exhausted; add a payment method under Billing.
- 429 — rate limit or demo cap hit; slow down or use a key.
- Sample rates are fixed — 24 kHz for TTS, 16 kHz for STT, mono, 16-bit signed little-endian.
- Reconnect with backoff — on unexpected close, retry with exponential backoff; sockets are stateless between utterances.