Kalpa LabsDocs
DashboardGet API key
Reference

API reference

Every endpoint, field and error — generated from the committed OpenAPI contract.

Kalpa Speech API v0.1.0, generated from the committed contract (openapi.json). Base URL https://api.kalpalabs.ai; bodies are JSON; authenticated endpoints take Authorization: Bearer $KALPA_API_KEY. HTTP errors before streaming begins use the envelope described in Rate limits & errors. Each endpoint except the WebSocket runs from this page: fill the fields, hit send — the request and response stay in sync on the right, copyable as curl, Python or JavaScript.

Not set

Kept in this browser, sent only to api.kalpalabs.ai. Get a key

POST/v1/tts/{voice_id}

Synthesize speech from text in a named voice.

Render the given text as speech (mono WAV, 24 kHz unless sample_rate asks for another rate) in one of the named voices from GET /v1/voices.

Path

voice_idrequiredstring

A named voice id, as listed by GET /v1/voices.

Body

textrequiredstring · 1 – 8000 chars

Text to speak; must contain at least one non-whitespace character.

modelstring | null

Public model id (see GET /v1/models). Omit/null for the default model.

output_format"wav" · default "wav"

Encoding of audio.data_b64: a 16-bit PCM WAV file, mono.

paramsobject

Generation knobs, all optional — the defaults are the tuned production sampling, shared by every generation endpoint (served with UI ranges at GET /v1/info).

params.temperaturenumber · 0 – 1.5 · default 0.9
params.acoustic_temperaturenumber | null · 0 – 1.5

Temperature for the voice's fine acoustic detail; null = follow temperature.

params.audio_qualitystring · low | medium | high · default "high"

Playback fidelity: high = full quality (default); lower tiers reduce fidelity, not payload size.

sample_rateinteger · 8000 | 16000 | 22050 | 24000 | 32000 | 44100 | 48000 · default 24000

Output sample rate in Hz.

Response 200 · TtsResponse

audioobject
audio.audio_qualitystring · low | medium | high

Fidelity tier this audio was rendered at (high = full quality).

audio.data_b64string

Base64-encoded 16-bit PCM WAV (mono).

audio.sample_rateinteger

Sample rate of the audio in Hz.

audio.formatstring

Container/encoding of data_b64 (16-bit PCM WAV).

modelstring

Resolved public model id that served the request.

request_idstring

Correlation id; identical to the X-Request-ID response header.

textstring

The text that was spoken (echoes the request).

usageobject
usage.input_audio_secondsnumber

Seconds of input audio supplied (converse).

usage.input_charsinteger

Characters of input text billed for this request.

usage.output_audio_secondsnumber

Seconds of audio generated.

metaobject

Backend-specific diagnostics (latency, frames, …).

Errors

400Semantically invalid request.
401Missing or invalid API key.
402Your organization's allowance of generated audio is used up or has expired.
403The organization is suspended.
404Unknown voice id, or a voice not offered for the requested model.
422Request validation failed.
429Rate limit exceeded.
500Unexpected internal error.
502The inference backend failed.
503Temporarily unavailable: the model is overloaded (`overloaded`; retry after `Retry-After`), or API-key verification is down (`service_unavailable`).
curl -s https://api.kalpalabs.ai/v1/tts/dda5319f-3b81-4124-b69e-9241eb510b77 \
  -H "Authorization: Bearer $KALPA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "text": "Hey there! How are you doing today?"
}'
200Example
0:02
{
  "request_id": "c77d02cb8b184744aa9cd8ad40a38866",
  "model": "kalpa-tts-1",
  "text": "Hey there! How are you doing today?",
  "audio": {
    "format": "wav",
    "sample_rate": 24000,
    "audio_quality": "high",
    "data_b64": "UklGRiQAAABXQVZFZm10…"
  },
  "usage": {
    "input_chars": 35,
    "input_audio_seconds": 0,
    "output_audio_seconds": 2.4
  },
  "meta": {}
}

POST/v1/tts/{voice_id}/stream

Stream speech in a named voice as raw PCM.

Render the given text as speech in one of the named voices from GET /v1/voices, streaming the audio while it is generated, so playback can start after the first ~80 ms instead of after the whole clip. The response body is the audio itself: raw 16-bit little-endian PCM, mono, at sample_rate (24 kHz by default), with no header. Each request renders one utterance. The response starts with the first audio, so a request that fails before then gets an ordinary error response with its status code; if generation fails after audio has started, the connection is terminated before the response completes. Closing the connection stops generation.

Path

voice_idrequiredstring

A named voice id, as listed by GET /v1/voices.

Body

textrequiredstring · 1 – 8000 chars

Text to speak; must contain at least one non-whitespace character.

modelstring | null

Public model id (see GET /v1/models). Omit/null for the default model.

output_format"pcm_s16le" · default "pcm_s16le"

Encoding of the response body: raw 16-bit little-endian PCM, mono, no header.

paramsobject

Generation knobs, all optional — the defaults are the tuned production sampling, shared by every generation endpoint (served with UI ranges at GET /v1/info).

params.temperaturenumber · 0 – 1.5 · default 0.9
params.acoustic_temperaturenumber | null · 0 – 1.5

Temperature for the voice's fine acoustic detail; null = follow temperature.

params.audio_qualitystring · low | medium | high · default "high"

Playback fidelity: high = full quality (default); lower tiers reduce fidelity, not payload size.

sample_rateinteger · 8000 | 16000 | 22050 | 24000 | 32000 | 44100 | 48000 · default 24000

Output sample rate in Hz.

Response 200 · audio/pcm

The audio itself, streamed as it is generated: raw 16-bit little-endian PCM, mono, at the requested sample_rate, with no header.

Errors

400Semantically invalid request.
401Missing or invalid API key.
402Your organization's allowance of generated audio is used up or has expired.
403The organization is suspended.
404Unknown voice id, or a voice not offered for the requested model.
422Request validation failed.
429Rate limit exceeded.
500Unexpected internal error.
502The inference backend failed.
503Temporarily unavailable: the model is overloaded (`overloaded`; retry after `Retry-After`), or API-key verification is down (`service_unavailable`).
curl -fsSN https://api.kalpalabs.ai/v1/tts/dda5319f-3b81-4124-b69e-9241eb510b77/stream \
  -H "Authorization: Bearer $KALPA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "text": "Hey there! How are you doing today?"
}' \
  --output speech.pcm
200Example
(raw 16-bit little-endian PCM, mono, 24 kHz: the audio bytes themselves, streamed as they are generated)

WSS/v1/tts/{voice_id}/stream

Stream speech in a named voice over WebSocket. Stream text and flush to hear each utterance in the named voice, independently per flush.

Use one connection for one named voice (voice_id as listed by GET /v1/voices). Start with initializeConnection, then stream text with sendText and set flush to true to hear it. Each flushed utterance is generated independently in the connection's voice — no state carries from one response to the next. One response generates at a time; a flush sent mid-generation is queued (up to 20 deep) and starts once the responses ahead of it settle, in flush order, and a bare cancelResponse abandons everything undelivered — the in-flight generation, every queued utterance, and buffered text. Every cancelResponse gets exactly one cancelled unless the connection closes first, after the last message of the response it stopped, so the connection can be reused right away. With generation_config, an utterance that fails before the server has its flush ends the connection: a fatal error, then close 1011, and no responseDone.

Client → server

initializeConnectionStarts and authenticates the session. Send this message first.11 fields
typerequired"initializeConnection"
api_keyrequiredstring

Your Kalpa API key.

modelstring | null · default null

Model id. Omit for the default model.

output_format"pcm_s16le" · default "pcm_s16le"

Encoding of responseAudio chunks: raw 16-bit little-endian PCM, mono.

sample_rateinteger · 8000 | 16000 | 22050 | 24000 | 32000 | 44100 | 48000 · default 24000

Output sample rate in Hz.

generation_configobject | null · default null

Enable and tune server-side segmentation of streamed text. Omit to generate only on flush.

generation_config.chunk_length_scheduleinteger[] · 1 – 10 items

Buffered-character thresholds before each successive segment of an utterance may start generating; the last value repeats. Lower values trade prosodic context for earlier first audio.

paramsobject

Generation parameters for every response on this connection.

params.acoustic_temperaturenumber | null · 0 – 1.5 · default null

Temperature for the voice's fine acoustic detail; null = follow temperature.

params.audio_qualitystring · low | medium | high · default "high"

Playback fidelity: high = full quality (default); lower tiers reduce fidelity, not payload size.

params.temperaturenumber · 0 – 1.5 · default 0.9
sendTextAdds text to the pending utterance. Set flush to true to generate its audio. A flush that arrives while a response is still generating is queued (up to 20 deep) and starts once the responses ahead of it settle, in flush order; only a flush arriving while 20 are already queued is rejected, and its text is discarded. A flush whose utterance is empty or whitespace-only gets a completed response with no audio and zero usage (nothing is billed).3 fields
typerequired"sendText"
textstring · default ""

Text to append to the utterance.

flushboolean · default false

Ends the utterance and starts generation when true.

cancelResponseStops the current response. Without response_id it abandons everything undelivered — the in-flight generation, every queued utterance, and buffered text (nothing from an abandoned turn can leak into the next one; discarded queued utterances produce no responseDone). With response_id it cancels that specific response only, leaving buffer and queue intact. Every cancelResponse is answered by exactly one cancelled unless the connection closes first.2 fields
typerequired"cancelResponse"
response_idstring | null · default null

Response to cancel. Omit to cancel the current response.

Server → client

sessionCreatedConfirms that the session is ready.8 fields
type"sessionCreated"
session_idstring

Id for this connection.

modelstring

Model serving the session.

voice_idstring

The named voice every response speaks in.

output_format"pcm_s16le"

Audio encoding for response chunks.

sample_rateinteger

Response audio sample rate in Hz.

channelsinteger
audio_qualitystring · low | medium | high

Fidelity tier responses are rendered at.

responseCreatedConfirms that response generation has started.4 fields
type"responseCreated"
response_idstring

Id for this response.

sample_rateinteger
audio_qualitystring · low | medium | high
responseAudioA chunk of generated audio.4 fields
pcm_b64string

Base64-encoded raw PCM audio.

response_idstring
frameinteger | null

Backend frame counter; informational.

type"responseAudio"
responseDoneConfirms that the response has completed or was cancelled.9 fields
type"responseDone"
response_idstring
statusstring · completed | cancelled
textstring

Text for the response.

usageobject
usage.input_charsinteger

Characters of input text billed for this request.

usage.input_audio_secondsnumber

Seconds of input audio supplied (converse).

usage.output_audio_secondsnumber

Seconds of audio generated.

metaobject

Public generation timing and runtime metadata.

cancelledAcknowledges a cancelResponse. Every cancelResponse gets exactly one unless the connection closes first, in the order sent: right after the responseDone of the response it stopped, or at once when it stopped nothing (idle, or a response_id that is not generating). Nothing the cancel abandoned sends anything after it, so the connection is ready for the next utterance.2 fields
type"cancelled"
response_idstring | null

The response_id the cancelResponse named, echoed. Absent for a bare cancel.

errorReports a WebSocket error.7 fields
type"error"
fatalboolean

True when the connection is about to close.

errorobject
error.typestring
error.messagestring
error.request_idstring | null

Id for the session or response.

response_idstring | null

Present when the error belongs to a specific generation.

Close codes

CodeMeaningClient action
1011Server-side failure; the fatal `error` frame before the close says which.Reconnect and retry.
4400Protocol violation.Fix the message before reconnecting.
4401Authentication failed.Check the API key.
4402The organization's free allowance of generated audio is used up or has lapsed.Do not retry; contact hello@kalpalabs.ai to keep generating.
4403The organization is suspended.Do not retry; contact hello@kalpalabs.ai.
4404Unknown voice id, or a voice not offered for the connection's model.See `GET /v1/voices?model=<model>` for that model's voices.
4408Initialization or idle timeout.Reconnect.
4429Reserved for rate limiting; not currently emitted.Retry with backoff.
4503API-key verification is temporarily unavailable.Retry with the same key.
# pip install websockets
import asyncio, base64, json, os
import websockets

VOICE_ID = "…"  # a voice id from GET /v1/voices

async def main():
    url = f"wss://api.kalpalabs.ai/v1/tts/{VOICE_ID}/stream"
    async with websockets.connect(url) as ws:
        await ws.send(json.dumps({
            "type": "initializeConnection",
            "api_key": os.environ["KALPA_API_KEY"],
        }))
        await ws.recv()  # sessionCreated
        await ws.send(json.dumps({
            "type": "sendText",
            "text": "Hello from Kalpa.", "flush": True,
        }))

        audio = bytearray()
        async for raw in ws:
            message = json.loads(raw)
            if message["type"] == "responseAudio":
                audio += base64.b64decode(message["pcm_b64"])
            elif message["type"] == "responseDone":
                break

        open("out.pcm", "wb").write(audio)

asyncio.run(main())

Both clients save the streamed mono, 16-bit PCM audio to out.pcm. Use the sample rate returned by sessionCreated for playback.

POST/v1/converse

Complete the open (final) turn of a conversation.

Given a conversation, complete its last ('open') turn. A speaker-only open turn is authored (text + audio); an open turn with text is rendered as that speaker, conditioned on the prior turns (contextual TTS).

Body

conversationrequiredarray · 1 – 64 items

The conversation, oldest turn first; the last turn is the open turn to complete.

conversation[].speakerstring · default "0"

Role label for this turn (one of the model's speakers).

conversation[].textstring | null · ≤ 8000 chars

Text spoken in this turn, if known.

conversation[].audio_wav_b64string | null

Base64 16-bit PCM WAV of this turn's audio, if any.

Turn 1
Open turnthe model completes it
modelstring | null

Public model id (see GET /v1/models). Omit/null for the default model.

paramsobject

Generation knobs, all optional — the defaults are the tuned production sampling, shared by every generation endpoint (served with UI ranges at GET /v1/info).

params.temperaturenumber · 0 – 1.5 · default 0.9
params.acoustic_temperaturenumber | null · 0 – 1.5

Temperature for the voice's fine acoustic detail; null = follow temperature.

params.audio_qualitystring · low | medium | high · default "high"

Playback fidelity: high = full quality (default); lower tiers reduce fidelity, not payload size.

Response 200 · ConverseResponse

modelstring

Resolved public model id that served the request.

replyobject
reply.speakerstring
reply.textstring
reply.audioobject | null
reply.audio.audio_qualitystring · low | medium | high

Fidelity tier this audio was rendered at (high = full quality).

reply.audio.data_b64string

Base64-encoded 16-bit PCM WAV (mono).

reply.audio.sample_rateinteger

Sample rate of the audio in Hz.

reply.audio.formatstring

Container/encoding of data_b64 (16-bit PCM WAV).

request_idstring

Correlation id; identical to the X-Request-ID response header.

usageobject
usage.input_audio_secondsnumber

Seconds of input audio supplied (converse).

usage.input_charsinteger

Characters of input text billed for this request.

usage.output_audio_secondsnumber

Seconds of audio generated.

metaobject

Errors

400Semantically invalid request.
401Missing or invalid API key.
402Your organization's allowance of generated audio is used up or has expired.
403The organization is suspended.
422Request validation failed.
429Rate limit exceeded.
500Unexpected internal error.
502The inference backend failed.
503Temporarily unavailable: the model is overloaded (`overloaded`; retry after `Retry-After`), or API-key verification is down (`service_unavailable`).
curl -s https://api.kalpalabs.ai/v1/converse \
  -H "Authorization: Bearer $KALPA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "conversation": [
    {
      "speaker": "0",
      "text": "Hi, who are you?"
    },
    {
      "speaker": "1"
    }
  ]
}'
200Example
{
  "request_id": "4b81de599ee148bc9df8327ec9ed1810",
  "model": "kalpa-tts-1",
  "reply": {
    "speaker": "1",
    "text": "I'm a speech model built by Kalpa Labs.",
    "audio": {
      "format": "wav",
      "sample_rate": 24000,
      "audio_quality": "high",
      "data_b64": "UklGRiQAAABXQVZFZm10…"
    }
  },
  "usage": {
    "input_chars": 16,
    "input_audio_seconds": 0,
    "output_audio_seconds": 3.2
  },
  "meta": {}
}

POST/v1/converse/stream

Complete the open turn, streaming audio as it is generated.

Same request as /v1/converse; the response is a text/event-stream: one meta event, audio chunk events as audio is generated (pcm_b64 = base64 raw 16-bit LE PCM at sample_rate), then exactly one end (reply text, audio_seconds, usage, meta) or error. First audio arrives after one ~80 ms frame instead of after the whole clip; closing the connection aborts generation.

Body

conversationrequiredarray · 1 – 64 items

The conversation, oldest turn first; the last turn is the open turn to complete.

conversation[].speakerstring · default "0"

Role label for this turn (one of the model's speakers).

conversation[].textstring | null · ≤ 8000 chars

Text spoken in this turn, if known.

conversation[].audio_wav_b64string | null

Base64 16-bit PCM WAV of this turn's audio, if any.

Turn 1
Open turnthe model completes it
modelstring | null

Public model id (see GET /v1/models). Omit/null for the default model.

paramsobject

Generation knobs, all optional — the defaults are the tuned production sampling, shared by every generation endpoint (served with UI ranges at GET /v1/info).

params.temperaturenumber · 0 – 1.5 · default 0.9
params.acoustic_temperaturenumber | null · 0 – 1.5

Temperature for the voice's fine acoustic detail; null = follow temperature.

params.audio_qualitystring · low | medium | high · default "high"

Playback fidelity: high = full quality (default); lower tiers reduce fidelity, not payload size.

Response 200 · text/event-stream

One meta event, audio events as audio is generated (base64 raw 16-bit PCM at meta.sample_rate), then exactly one end or error.

Errors

400Semantically invalid request.
401Missing or invalid API key.
402Your organization's allowance of generated audio is used up or has expired.
403The organization is suspended.
422Request validation failed.
429Rate limit exceeded.
500Unexpected internal error.
502The inference backend failed.
503Temporarily unavailable: the model is overloaded (`overloaded`; retry after `Retry-After`), or API-key verification is down (`service_unavailable`).
curl -sN https://api.kalpalabs.ai/v1/converse/stream \
  -H "Authorization: Bearer $KALPA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "conversation": [
    {
      "speaker": "0",
      "text": "Hi, who are you?"
    },
    {
      "speaker": "1"
    }
  ]
}'
200Example
event: meta
data: {"model":"kalpa-tts-1","mode":"converse_author","sample_rate":24000,"audio_quality":"high","context_turns":1}

event: audio
data: {"pcm_b64":"…","frame":1}

event: end
data: {"request_id":"4b81de599ee148bc9df8327ec9ed1810","text":"I'm a speech model built by Kalpa Labs.","sample_rate":24000,"audio_quality":"high","audio_seconds":3.2,"usage":{"input_chars":16,"input_audio_seconds":0.0,"output_audio_seconds":3.2},"meta":{"mode":"converse_author","context_turns":1}}

WSS/v1/converse/stream

Stream a stateful conversation over WebSocket. Send JSON messages to build the conversation and receive audio as it is generated.

Use one connection for one conversation. Start with initializeConnection, then send text or add completed turns. The server keeps the conversation history until the socket closes and returns generated audio as responseAudio messages.

Client → server

initializeConnectionStarts and authenticates the WebSocket session. Send this message first.8 fields
typerequired"initializeConnection"
api_keyrequiredstring

Your Kalpa API key.

modelstring | null · default null

Model id. Omit for the default model.

history_limitinteger | null · 1 – 64 · default null

Optional rolling number of completed turns retained for each response.

paramsobject

Generation parameters for every response on this connection.

params.acoustic_temperaturenumber | null · 0 – 1.5 · default null

Temperature for the voice's fine acoustic detail; null = follow temperature.

params.audio_qualitystring · low | medium | high · default "high"

Playback fidelity: high = full quality (default); lower tiers reduce fidelity, not payload size.

params.temperaturenumber · 0 – 1.5 · default 0.9
sendTextAdds text to the current turn. Set flush to true to generate the response.4 fields
typerequired"sendText"
speaker_idinteger | null · ≥ 0 · default null

Speaker for this turn. Required on the first sendText message.

textstring · default ""

Text to append to the turn.

flushboolean · default false

Ends the turn and starts generation when true.

addTurnAdds a completed text-and-audio turn to the conversation history.4 fields
typerequired"addTurn"
speaker_idrequiredinteger · ≥ 0
textrequiredstring · ≥ 1 chars

Transcript for the audio.

audio_wav_b64requiredstring · ≥ 1 chars

Base64-encoded WAV audio.

cancelResponseStops the current response.2 fields
typerequired"cancelResponse"
response_idstring | null · default null

Response to cancel. Omit to cancel the current response.

Server → client

sessionCreatedConfirms that the session is ready.7 fields
type"sessionCreated"
session_idstring

Id for this conversation.

modelstring

Model serving the session.

output_format"pcm_s16le"

Audio encoding for response chunks.

sample_rateinteger

Response audio sample rate in Hz.

channelsinteger
audio_qualitystring · low | medium | high

Fidelity tier responses are rendered at.

responseCreatedConfirms that response generation has started.5 fields
type"responseCreated"
response_idstring

Id for this response.

speaker_idinteger

Speaker for this response.

sample_rateinteger
audio_qualitystring · low | medium | high
responseAudioA chunk of generated audio.4 fields
pcm_b64string

Base64-encoded raw PCM audio.

response_idstring
frameinteger | null

Backend frame counter; informational.

type"responseAudio"
responseDoneConfirms that the response has completed or was cancelled.10 fields
type"responseDone"
response_idstring
speaker_idinteger
statusstring · completed | cancelled
textstring

Text for the response.

usageobject
usage.input_charsinteger

Characters of input text billed for this request.

usage.input_audio_secondsnumber

Seconds of input audio supplied (converse).

usage.output_audio_secondsnumber

Seconds of audio generated.

metaobject

Public generation timing and runtime metadata.

errorReports a WebSocket error.7 fields
type"error"
fatalboolean

True when the connection is about to close.

errorobject
error.typestring
error.messagestring
error.request_idstring | null

Id for the session or response.

response_idstring | null

Present when the error belongs to a specific generation.

Close codes

CodeMeaningClient action
1011Server-side failure; the fatal `error` frame before the close says which.Reconnect and retry.
4400Protocol violation.Fix the message before reconnecting.
4401Authentication failed.Check the API key.
4402The organization's free allowance of generated audio is used up or has lapsed.Do not retry; contact hello@kalpalabs.ai to keep generating.
4403The organization is suspended.Do not retry; contact hello@kalpalabs.ai.
4408Initialization or idle timeout.Reconnect.
4429Reserved for rate limiting; not currently emitted.Retry with backoff.
4503API-key verification is temporarily unavailable.Retry with the same key.
# pip install websockets
import asyncio, base64, json, os
import websockets

async def main():
    async with websockets.connect("wss://api.kalpalabs.ai/v1/converse/stream") as ws:
        await ws.send(json.dumps({
            "type": "initializeConnection",
            "api_key": os.environ["KALPA_API_KEY"],
        }))
        await ws.recv()  # sessionCreated
        await ws.send(json.dumps({
            "type": "sendText", "speaker_id": 0,
            "text": "Hello from Kalpa.", "flush": True,
        }))

        audio = bytearray()
        async for raw in ws:
            message = json.loads(raw)
            if message["type"] == "responseAudio":
                audio += base64.b64decode(message["pcm_b64"])
            elif message["type"] == "responseDone":
                break

        open("out.pcm", "wb").write(audio)

asyncio.run(main())

Both clients save the streamed mono, 16-bit PCM audio to out.pcm. Use the sample rate returned by sessionCreated for playback.

GET/v1/models

List available public models.

Response 200 · ModelsResponse

dataarray

The available public models.

data[].display_namestring

Human-readable model name.

data[].idstring

Stable public model id used in the model request field.

data[].modesstring[]

Supported modes: subset of ["converse", "tts"].

data[].speakersstring[]

Valid role labels for a turn's speaker, in turn order (e.g. ["0", "1"]).

data[].defaultboolean

True for the model used when model is omitted.

data[].descriptionstring

What this model is for.

data[].languagesstring[]

Languages the model speaks, as ISO 639-1 codes (e.g. ["en", "hi"]).

Errors

401Missing or invalid API key.
403The organization is suspended.
500Unexpected internal error.
503API-key verification is temporarily unavailable.
curl -s https://api.kalpalabs.ai/v1/models \
  -H "Authorization: Bearer $KALPA_API_KEY"
200Example
{
  "data": [
    {
      "id": "kalpa-tts-1",
      "display_name": "Kalpa TTS 1",
      "description": "Speech model (TTS + dialogue) — English and Hindi; the default.",
      "modes": [
        "converse",
        "tts"
      ],
      "speakers": [
        "0",
        "1"
      ],
      "languages": [
        "en",
        "hi"
      ],
      "default": true
    },
    {
      "id": "kalpa-tts-beta-v0.1",
      "display_name": "Kalpa TTS beta v0.1",
      "description": "English conversational speech model (dialogue + TTS).",
      "modes": [
        "converse",
        "tts"
      ],
      "speakers": [
        "0",
        "1"
      ],
      "languages": [
        "en"
      ],
      "default": false
    }
  ]
}

GET/v1/voices

List available named voices.

Query

modelstring | null

Only the voices offered for this public model id (aliases accepted); omit for the whole catalog.

Response 200 · VoicesResponse

dataarray

The available named voices.

data[].genderstring · feminine | masculine

Perceived voice gender.

data[].idstring

Stable public voice id, used as the /v1/tts/{voice_id} path segment.

data[].modelsstring[]

Public model ids (GET /v1/models) this voice is offered for. POST /v1/tts/{voice_id} and WSS /v1/tts/{voice_id}/stream answer 404 for any other model.

data[].namestring

Human-readable voice name.

Errors

400Unknown `model`.
401Missing or invalid API key.
403The organization is suspended.
422Validation Error
500Unexpected internal error.
503API-key verification is temporarily unavailable.
curl -s https://api.kalpalabs.ai/v1/voices \
  -H "Authorization: Bearer $KALPA_API_KEY"
200Example
{
  "data": [
    {
      "id": "dda5319f-3b81-4124-b69e-9241eb510b77",
      "name": "Wren (human-like)",
      "gender": "feminine",
      "models": [
        "kalpa-tts-1",
        "kalpa-tts-beta-v0.1"
      ]
    },
    {
      "id": "c08d0442-e96a-471d-808e-43563061a508",
      "name": "Cole",
      "gender": "masculine",
      "models": [
        "kalpa-tts-beta-v0.1"
      ]
    }
  ]
}

GET/v1/info

Backend info, default params, and limits.

Response 200 · InfoResponse

backendobject

Active gateway/backend description.

backend.detailstring
backend.kindstring
backend.namestring
backend.readyboolean
backend.sample_rateinteger
defaultsobject

Default generation parameters.

defaults.acoustic_temperaturenumber | null · 0 – 1.5

Temperature for the voice's fine acoustic detail; null = follow temperature.

defaults.audio_qualitystring · low | medium | high

Playback fidelity: high = full quality (default); lower tiers reduce fidelity, not payload size.

defaults.temperaturenumber · 0 – 1.5
limitsobject

Effective request caps and rate limits for this key.

limits.max_audio_bytesinteger
limits.max_conversation_turnsinteger
limits.max_text_charsinteger
limits.rate_limit_burstinteger
limits.rate_limit_rpminteger
param_schemaarray

UI metadata for the generation parameters.

param_schema[].helpstring
param_schema[].keystring
param_schema[].labelstring
param_schema[].typestring · slider | select | int
param_schema[].deprecatedboolean
param_schema[].maxnumber
param_schema[].minnumber
param_schema[].nullableboolean
param_schema[].offLabelstring
param_schema[].optionsinteger | string[]
param_schema[].stepnumber

Errors

401Missing or invalid API key.
403The organization is suspended.
500Unexpected internal error.
503API-key verification is temporarily unavailable.
curl -s https://api.kalpalabs.ai/v1/info \
  -H "Authorization: Bearer $KALPA_API_KEY"
200Example
{
  "backend": {
    "name": "Kalpa gateway (http)",
    "kind": "http",
    "ready": true,
    "sample_rate": 24000,
    "detail": "default=kalpa-tts-1 · variants=2"
  },
  "defaults": {
    "temperature": 0.9,
    "acoustic_temperature": null,
    "audio_quality": "high"
  },
  "limits": {
    "max_text_chars": 8000,
    "max_conversation_turns": 64,
    "max_audio_bytes": 26214400,
    "rate_limit_rpm": 600,
    "rate_limit_burst": 120
  },
  "param_schema": [
    {
      "key": "temperature",
      "label": "Temperature",
      "type": "slider",
      "min": 0,
      "max": 1.5,
      "step": 0.05,
      "help": "Sampling temperature for the words and delivery. 0 = deterministic."
    },
    {
      "key": "acoustic_temperature",
      "label": "Acoustic temperature",
      "type": "slider",
      "min": 0,
      "max": 1.5,
      "step": 0.05,
      "nullable": true,
      "offLabel": "linked",
      "help": "Temperature for the voice's fine acoustic detail. Linked to Temperature when unset."
    },
    {
      "key": "audio_quality",
      "label": "Audio quality",
      "type": "select",
      "options": [
        "low",
        "medium",
        "high"
      ],
      "help": "Playback fidelity. High is full quality; lower tiers reduce fidelity, not payload size."
    }
  ]
}

GET/v1/usage

Your metered usage.

Durable usage totals (requests, input characters, audio seconds) for the calling API key. Without parameters: lifetime totals, plus organization, your organization's generated audio against its allowance. With start_time/end_time (ISO 8601): totals for that window at hourly granularity; the effective window snaps outward to hour boundaries and is echoed back.

Query

start_timestring | null

Window start (ISO 8601; naive = UTC). Snapped down to the hour. Omit both bounds for lifetime totals.

end_timestring | null

Window end (ISO 8601, exclusive; naive = UTC). Snapped up to the hour. Defaults to now when only start_time is given.

Response 200 · UsageSummaryResponse

input_audio_secondsnumber
input_charsinteger
key_idstring

Non-secret identifier of the API key whose usage is summarized.

output_audio_secondsnumber
requestsinteger
end_timestring | null

Effective (hour-snapped) window end, exclusive; windowed queries only.

first_request_tsnumber | null

Unix time of the first metered request (lifetime queries).

last_request_tsnumber | null

Unix time of the latest metered request (lifetime queries).

organizationobject | null

Your organization's generated audio against its allowance. Lifetime queries only.

organization.exhaustedboolean

True when the allowance is used up or has expired. Generation requests then return 402.

organization.org_idstring

Identifier of the organization that owns the key.

organization.output_audio_secondsnumber

Seconds of audio generated so far by all keys of the organization.

organization.allowance_expires_atstring | null

When the allowance expires. Absent if it never expires.

organization.allowance_secondsnumber | null

Seconds of audio the organization may generate in total. Absent if there is no cap.

start_timestring | null

Effective (hour-snapped) window start; windowed queries only.

Errors

400Semantically invalid request.
401Missing or invalid API key.
403The organization is suspended.
422Validation Error
500Unexpected internal error.
503Key verification or usage reporting is temporarily unavailable.
curl -s https://api.kalpalabs.ai/v1/usage \
  -H "Authorization: Bearer $KALPA_API_KEY"
200Example
{
  "key_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  "requests": 1284,
  "input_chars": 91230,
  "input_audio_seconds": 411,
  "output_audio_seconds": 3120.5,
  "last_request_ts": 1751500000,
  "organization": {
    "org_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
    "output_audio_seconds": 4890.2,
    "allowance_seconds": 60000,
    "allowance_expires_at": "2027-01-11T03:43:35Z",
    "exhausted": false
  }
}

GET/health

Liveness probe. No authentication.

Response 200 · HealthResponse

backendstring
readyboolean
statusstring

Errors

500Unexpected internal error.
curl -s https://api.kalpalabs.ai/health
200Example
{
  "status": "ok",
  "backend": "http",
  "ready": true
}