Text-to-speech
Turn text into speech with POST /v1/tts, and tune generation with the sampling knobs.
POST /v1/tts renders text as speech in a single call:
curl -s https://api.kalpalabs.ai/v1/tts \
-H "Authorization: Bearer $KALPA_API_KEY" -H 'Content-Type: application/json' \
-d '{
"text": "Hey there! How are you doing today?",
"speaker": "0",
"model": "kalpa-tts-beta-v0.1",
"params": {"temperature": 0.9}
}'| Field | Notes |
|---|---|
text | 1 – 8,000 characters. |
speaker | A role label from the model's speakers (defaults to "0"). Labels are model-specific — see Models. |
model | A public model id; omit for the default. |
params | Sampling knobs, all optional — see below. |
Semantically, TTS is a one-turn conversation: it is equivalent to /v1/converse with [{"speaker": "0", "text": "…"}] as the whole conversation. /v1/tts remains the simpler convenience endpoint; use converse directly when you have prior turns to condition on.
Hindi
kalpa-tts-multilingual-beta-v0.1 speaks Hindi as well as English — send Devanagari text (naturally code-switched English words are fine) and select the model:
curl -s https://api.kalpalabs.ai/v1/tts \
-H "Authorization: Bearer $KALPA_API_KEY" -H 'Content-Type: application/json' \
-d '{
"text": "नमस्ते, कल्पा में आपका स्वागत है! बताइए, आज आपके लिए क्या किया जाए?",
"model": "kalpa-tts-multilingual-beta-v0.1"
}'Each model card's languages lists what it speaks — see Models.
Named voices
Bare /v1/tts picks a plausible voice per request. To render in a stable, reusable voice, list the catalog and address one by id:
curl -s https://api.kalpalabs.ai/v1/voices -H "Authorization: Bearer $KALPA_API_KEY"
# {"data": [{"id": "8636013a-…", "name": "June", "gender": "feminine"}, …]}
curl -s https://api.kalpalabs.ai/v1/tts/8636013a-4e31-4cd4-b706-653d854a714b \
-H "Authorization: Bearer $KALPA_API_KEY" -H 'Content-Type: application/json' \
-d '{"text": "Hey there! How are you doing today?"}'The request body is /v1/tts minus speaker (the voice determines it), and the response is the same shape as /v1/tts. Each voice is a short reference clip the model continues in context — the same conversational conditioning as /v1/converse, behind a voice id.
For live playback, the streaming WebSocket — WSS /v1/tts/{voice_id}/stream — renders the same named voice utterance by utterance: stream text with sendText, set flush: true to hear it. Two behaviors are built for voice agents: a flush sent while a response is still generating is queued (one slot) and starts the moment it settles, and a bare cancelResponse is a barge-in — it abandons everything undelivered: the in-flight generation, the queued utterance, and any unflushed text. A cancelResponse naming a response_id cancels just that response.
When your text arrives incrementally (an LLM streaming a reply), add generation_config to initializeConnection and audio starts before the flush: once buffered text crosses the next chunk_length_schedule threshold (default [50, 80, 120, 150] characters) and ends at a complete sentence, that part starts rendering while the rest keeps arriving. The utterance stays one response on the wire — one responseCreated, one responseDone, a continuous frame counter — and its parts keep continuous prosody; flush: true still marks the end. Lower thresholds mean earlier first audio; text with no sentence boundary is cut at a clause or word gap once it runs twice the threshold, so first audio stays bounded even for unpunctuated text.
The audio you get back
{
"request_id": "…",
"model": "kalpa-tts-beta-v0.1",
"text": "Hey there! How are you doing today?",
"audio": {
"format": "wav",
"sample_rate": 24000,
"audio_quality": "high",
"data_b64": "UklGRi…"
},
"usage": { "input_chars": 35, "input_audio_seconds": 0.0, "output_audio_seconds": 2.6 },
"meta": {}
}request_id matches the X-Request-ID response header and exists to correlate this call with client and server logs; see Request IDs.
data_b64 is a complete 16-bit PCM WAV file (mono, 24 kHz), base64-encoded — decode it and play:
import base64
with open("out.wav", "wb") as f:
f.write(base64.b64decode(reply["audio"]["data_b64"]))audio_quality echoes the fidelity tier the waveform was rendered at (see the audio_quality knob).
Generation parameters
All fields of params are optional; the defaults are the tuned starting point. The same knobs (with the live defaults and UI ranges) are served at GET /v1/info.
| Param | Default | Range | What it does |
|---|---|---|---|
temperature | 0.9 | 0 – 1.5 | Sampling temperature for the words and delivery. 0 = deterministic. |
acoustic_temperature | null | 0 – 1.5 | Temperature for the voice's fine acoustic detail. null = follows temperature. |
max_new_tokens | null | 16 – 2048 | Optional generation cap. By default, generation continues until a stop token or the model's remaining context window is used. |
audio_quality | "high" | low / medium / high | Playback fidelity. high is full quality; lower tiers reduce fidelity — not payload size (the WAV is always full-duration 16-bit PCM). |
One practical note: for reproducible output, set temperature to 0 — with greedy decoding the same request returns the same speech.
If generation reaches the model's context limit before a stop token, the API returns context_length_exceeded; try shorter sentences.