Text-to-Speech API

Stream text to the Krisp Text-to-Speech WebSocket endpoint and receive synthesized speech as it is generated.

🚧

Beta: Text-to-Speech is in beta. Models, messages and limits may change before general availability.

Stream text to Krisp over a WebSocket and receive synthesized speech as it is generated. Audio starts arriving before you finish sending text, which makes it a good fit for voice agents and other low-latency voice output.

See Authentication for how to generate a session token, and the API overview for the session protocol shared by all endpoints.

Try it

Paste an API key from the Developers Portal, pick a language and voice, and press Speak. The demo creates a short-lived session token from your key and streams the text to wss://streaming.krisp.ai/tts. Audio plays as it arrives. Sessions count toward your usage.

Connecting

  • URL: wss://streaming.krisp.ai/tts
  • Auth: a session token generated from your API key, sent as the Authorization: api-key SESSION_TOKEN header, or as the authorization query parameter if headers can't be set. Do not send your API key (master token) on the WebSocket. See Authentication for how to generate a session token.
  • First message: the client hello as a JSON text message, within 15 seconds of connecting. Wait for ready before sending text.
  • Sending text: after ready, send JSON text messages:
MessageMeaning
{"type": "text", "text": "..."}A piece of text to speak. You can stream a sentence in small pieces (the example clients send 4 words at a time).
{"type": "end_of_sentence"}Marks a sentence boundary between two sentences.
{"type": "end"}No more text will be sent in this session.
  • Receiving audio: synthesized audio arrives as binary messages. The Krisp example clients play and save these frames as 16-bit mono PCM at 24 kHz.
  • Finishing: after end, wait for the Done message, then close the WebSocket.

Output format: TTS audio is always 16-bit mono PCM at 24 kHz.

Client hello

TTSClientHello

The first message sent by the client to initiate a TTS connection.

FieldTypeRequiredDefaultDescription
modelstringyesModel ID: krisp-tts-v1.
voicestringyesName of a voice from the Krisp voice registry.
languagestringnoen-USSpoken language of the synthesized audio.
speednumbernothe voice's speedPlayback speed multiplier.
metadataobjectnoUser-provided metadata (max 4KB when JSON-encoded)

Constraints:

  • model must be krisp-tts-v1 (see Supported models).
  • voice, if set, must not be empty.
  • metadata must not exceed 4,096 bytes when JSON-encoded. It is echoed back in ready.

Rules for krisp-tts-v1. The hello is validated as follows:

FieldRule
voiceRequired. The name of a voice from the Krisp voice registry (case-insensitive). List the available voices with GET /v2/sdk/tts/voices in the Krisp SDK API.
languageOne of en-US, fr-FR, es-ES, es-US, de-DE, ru-RU (case-insensitive). Default: en-US. The voice must support the language.
speedOne of 0.9, 0.95, 1.0, 1.05, 1.1. If omitted, the voice's own configured speed is used (1.0 if it has none).

Example:

{
  "model": "krisp-tts-v1",
  "voice": "YOUR_VOICE_NAME",
  "language": "en-US",
  "speed": 1.0,
  "metadata": {"call_id": "abc-123"}
}

Server messages

TTSGatewayServerHello (ready)

The first response sent by the server to the client. For krisp-tts-v1 it echoes the settings the model applied, which can differ from the request when the model substituted a value.

FieldTypeRequiredDefaultDescription
typestringno"ready"Response type
request_idstring | nullnonullRequest ID if available
tracks_msarray of integer | nullnonullDelays in ms of the decoders producing partial results. Each partial word's src is one of these values.
voicestring | nullnonullApplied voice
languagestring | nullnonullApplied language
speednumber | nullnonullApplied speed
warningany | nullnonullWhy the applied settings differ from the request
metadataobject | nullnonullUser-provided metadata, echoed back. Omitted entirely when the service's ClientHello carries no metadata field.

ErrorResponse (error)

Error response schema following the standard format.

FieldTypeRequiredDefaultDescription
typestringno"error"Response type
request_idstring | nullnonullRequest ID if available
errorErrorInfoyesError information

ErrorInfo

Error information structure.

FieldTypeRequiredDefaultDescription
codestringyesError code identifier
status_codeintegeryesHTTP status code
messagestringyesHuman-readable error message
detailsobject | nullnonullOptional error details (e.g., {'hint': '...'})

Synthesis messages

After ready, everything the model sends is forwarded to you unchanged:

MessageMeaning
binary frameA chunk of synthesized audio.
{"type": "end_of_sentence", ...}End-of-sentence marker from the model.
{"type": "Done", ...}Synthesis is complete; no more audio follows.

Supported models

ModelNotes
krisp-tts-v1Voices come from the Krisp voice registry. voice is required, and language and speed are validated (see Rules for krisp-tts-v1).

Errors

Besides the common errors, these are specific to /tts:

error.codestatus_codeWhen
invalid_client_hello400Unknown model, a missing or empty voice, a language that is not in the supported list, an unsupported speed, or metadata larger than 4 KB. details.errors names the field.
invalid_voice400The requested voice does not exist. details.hint points to GET /v2/sdk/tts/voices.
invalid_language400The language is supported, but the requested voice does not support it. details.hint lists the languages the voice supports.
internal_server_error500The voice registry is temporarily unavailable. Retry after a few seconds.

These errors are sent before ready, and the connection is then closed with close code 1008.

Example

Streams two pieces of text and saves the audio to output.wav. Requires pip install "websockets>=14".

import asyncio
import json
import wave

import websockets

SESSION_TOKEN = "YOUR_SESSION_TOKEN"  # see the Authentication page
URL = "wss://streaming.krisp.ai/tts"


async def main() -> None:
    headers = {"Authorization": f"api-key {SESSION_TOKEN}"}
    async with websockets.connect(URL, additional_headers=headers) as ws:
        await ws.send(json.dumps({
            "model": "krisp-tts-v1",
            "voice": "YOUR_VOICE_NAME",
            "language": "en-US",
        }))
        hello = json.loads(await ws.recv())
        if hello["type"] != "ready":
            raise RuntimeError(hello)

        for piece in ["Hello! This is a streaming ", "text-to-speech test."]:
            await ws.send(json.dumps({"type": "text", "text": piece}))
        await ws.send(json.dumps({"type": "end"}))

        audio = bytearray()
        async for message in ws:
            if isinstance(message, bytes):
                audio.extend(message)
                continue
            event = json.loads(message)
            if event.get("type") == "error":
                raise RuntimeError(event)
            if event.get("type") == "Done":
                break

    with wave.open("output.wav", "wb") as wf:
        wf.setnchannels(1)
        wf.setsampwidth(2)
        wf.setframerate(24000)
        wf.writeframes(bytes(audio))


asyncio.run(main())

Changelog

  • 2026-10-01: Authentication now uses a session token; see Authentication.
  • 2026-10-01: Public model list limited to krisp-tts-v1.
  • 2026-10-01: Page created: client hello with krisp-tts-v1 rules, text and audio messages, supported models.

Did this page help you?