Guides
.md ↗

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:

bash
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}
  }'
FieldNotes
text1 – 8,000 characters.
speakerA role label from the model's speakers (defaults to "0"). Labels are model-specific — see Models.
modelA public model id; omit for the default.
paramsSampling 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:

bash
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:

bash
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 WebSocketWSS /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

json
{
  "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:

python
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.

ParamDefaultRangeWhat it does
temperature0.90 – 1.5Sampling temperature for the words and delivery. 0 = deterministic.
acoustic_temperaturenull0 – 1.5Temperature for the voice's fine acoustic detail. null = follows temperature.
max_new_tokensnull16 – 2048Optional generation cap. By default, generation continues until a stop token or the model's remaining context window is used.
audio_quality"high"low / medium / highPlayback 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.