Generate speech
POST https://api.kittenml.com/v1/audio/speech
Three sizes. Eight voices. One simple request. Generate speech with Nano, Micro, or Mini; all three hosted KittenTTS 0.8 models are free.
A valid TTS-enabled API key is still required. The key needs the tts:generate
permission so the generation can appear in your usage history at a cost of
$0.
Audio streams as it is generated. The default response is chunked binary audio; choose SSE when your client needs explicit, Base64-encoded audio events.
Request body
| Field | Type | Required | Default | Accepted values |
|---|---|---|---|---|
model | string | No | kitten-tts-mini-0.8 | kitten-tts-nano-0.8, kitten-tts-micro-0.8, or kitten-tts-mini-0.8 |
input | string | Yes | — | Non-empty text, at most 4,096 characters |
voice | string | No | Bella | One of the eight voices below |
response_format | string | No | mp3 | mp3, wav, flac, aac, opus, or pcm |
stream_format | string | No | audio | audio for binary chunks or sse for Server-Sent Events |
speed | number | No | 1.0 | 0.25 through 4.0 |
The JSON body may be at most 64 KiB. Unknown fields are not part of the public contract.
Voices
Meet the cast. Voice names are case-sensitive:
| Voice | Voice | Voice | Voice |
|---|---|---|---|
Bella | Jasper | Luna | Bruno |
Rosie | Hugo | Kiki | Leo |
Output formats
| Format | Content type | File extension | Notes |
|---|---|---|---|
mp3 | audio/mpeg | .mp3 | Compact default |
wav | audio/wav | .wav | 24 kHz mono PCM in a WAV container |
flac | audio/flac | .flac | Lossless compressed audio |
aac | audio/aac | .aac | AAC audio |
opus | audio/ogg | .opus or .ogg | Opus in an Ogg container |
pcm | audio/L16; rate=24000; channels=1 | .pcm | Headerless 24 kHz mono, little-endian PCM16 |
The pcm bytes are little-endian. Some media libraries interpret the standard
audio/L16 name as big-endian; use wav for portable PCM interchange, or
explicitly configure your raw-audio reader for signed 16-bit little-endian data.
Stream binary audio with cURL
curl --fail-with-body -sS \
https://api.kittenml.com/v1/audio/speech \
-H "Authorization: Bearer $KITTENML_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"model": "kitten-tts-mini-0.8",
"input": "Hello! This audio was generated by KittenTTS.",
"voice": "Bella",
"response_format": "mp3",
"stream_format": "audio",
"speed": 1.0
}' \
--output kitten-output.mp3
The success body is binary audio. cURL writes each received chunk to the file;
it does not need to hold the complete response in memory. Do not pipe binary
audio to jq or print it in a terminal.
Stream SSE events
Set stream_format to sse when you need event boundaries:
curl --fail-with-body -sS -N \
https://api.kittenml.com/v1/audio/speech \
-H "Authorization: Bearer $KITTENML_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"model": "kitten-tts-mini-0.8",
"input": "The first sentence can play while the next sentence is generated. This keeps longer responses responsive.",
"voice": "Bella",
"response_format": "mp3",
"stream_format": "sse"
}'
Each speech.audio.delta contains a Base64-encoded piece of the selected audio
format. Concatenate the decoded bytes in order. A final speech.audio.done
marks a successful, complete stream:
data: {"type":"speech.audio.delta","audio":"SUQzBAAAAA..."}
data: {"type":"speech.audio.delta","audio":"qqqqqqqqqq..."}
data: {"type":"speech.audio.done","usage":{"input_tokens":0,"output_tokens":0,"total_tokens":0}}
If the connection closes before speech.audio.done, treat the output as
incomplete and retry the generation with a new request.
Python with the OpenAI SDK
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["KITTENML_API_KEY"],
base_url="https://api.kittenml.com/v1",
)
with client.audio.speech.with_streaming_response.create(
model="kitten-tts-mini-0.8",
input="Hello from the KittenML API.",
voice="Bella",
response_format="mp3",
speed=1.0,
) as audio:
audio.stream_to_file("kitten-output.mp3")
JavaScript with the OpenAI SDK
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
import { createWriteStream } from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.KITTENML_API_KEY,
baseURL: "https://api.kittenml.com/v1",
});
const audio = await client.audio.speech.create({
model: "kitten-tts-mini-0.8",
input: "Hello from the KittenML API.",
voice: "Bella",
response_format: "mp3",
speed: 1.0,
});
await pipeline(
Readable.fromWeb(audio.body),
createWriteStream("kitten-output.mp3"),
);
Test every model
for MODEL in \
kitten-tts-nano-0.8 \
kitten-tts-micro-0.8 \
kitten-tts-mini-0.8
do
curl --fail-with-body -sS \
https://api.kittenml.com/v1/audio/speech \
-H "Authorization: Bearer $KITTENML_API_KEY" \
-H "Content-Type: application/json" \
--data "{\"model\":\"$MODEL\",\"input\":\"Testing $MODEL.\",\"voice\":\"Bella\",\"response_format\":\"wav\"}" \
--output "$MODEL.wav"
done
Successful response
With stream_format: audio, the response body contains chunked audio bytes.
Useful headers include:
| Header | Meaning |
|---|---|
x-request-id | Identifier for logs and support |
X-Model | Model that generated the response |
X-Stream-Format | audio or sse |
Content-Disposition | Suggested output filename |
Cache-Control | no-store |
Header names are case-insensitive.
Validation errors
Authentication and model errors use the common KittenML error envelope.
Parameter validation errors currently use a detail field:
{
"detail": "Unknown voice. Supported voices: Bella, Jasper, Luna, Bruno, Rosie, Hugo, Kiki, Leo"
}
See Errors and retries for client behavior.
Limits
An organization may run two TTS generations concurrently. A stream holds its
slot until it completes or disconnects. A request above the limit returns 429
with Retry-After: 1. See
Rate limits and timeouts.