Skip to main content
POST
Text to speech
Building with an AI agent? Point it at docs.kova.ai/for-agents.md — the complete API contract as one plain-text page.
POST /v1/tts is the simplest way to generate audio: send text, get a complete audio file back as base64. Use it when you have a single utterance and want the finished file. If you need playback to start before generation finishes, use Streaming TTS instead.
Generation runs at roughly 2× realtime, so this endpoint is slow on long input. Measured end-to-end: 500 characters ≈ 17 s, 1,000 ≈ 36 s, 2,000 ≈ 65 s, 5,000 ≈ 153 s. There is no server-side character limit, but a 10,000-character request exceeds a 180-second client timeout. Set generous timeouts, and prefer streaming above roughly 1,000 characters.

Request

The schema is strict. Any field not in this table returns 422 with "Extra inputs are not permitted". There is no forward compatibility for unknown keys — check spelling carefully.

Response

audio is always present and always base64 — decode it before writing bytes to disk. timestamps is present only when the request set timestamps: true; otherwise the key is absent or null. Its three arrays are parallel and equal in length: words[i] spans start_seconds[i] to end_seconds[i], measured in seconds from the start of the audio.
Word tokens keep their original punctuation — you’ll see "Kova." and "$19.99.", not bare words. normalize_text changes how text is pronounced but does not change these tokens.

Examples

response_format

response_format accepts an object or, as shorthand, a bare encoding string (e.g. "mp3"). All output is mono. There is no channel option.

bitrate is in kbps, never bps

"128k", "128" and 128 are all equivalent and all mean 128 kbps.
128000 is rejected with a 422. An integer bitrate is read as kilobits per second, so 128000 parses as 128,000 kbps — far out of range. Never pass bits per second.
Passing bitrate to wav, pcm, linear16, mulaw or alaw returns 422 with "<encoding> audio does not support a bitrate option". Omit the field for those encodings — sending null is fine.

Encoding names are case-insensitive and aliased

"MP3", "Mp3" and "mp3" are identical. Aliases are accepted and normalized:
The linear_pcm family resolves to linear16, which emits a RIFF header — not to headerless pcm. If you want raw samples, ask for pcm explicitly. Prefer the canonical names in the first table; aliases exist for compatibility only.
Anything else — flac, ogg, aac, or an empty string — returns 422 "Unsupported audio encoding".

Choosing an encoding

  • Saving a file for later playbackmp3 at defaults.
  • Telephony (Twilio, SIP)mulaw at 8000 Hz.
  • Feeding an audio pipeline or another servicepcm, and track the sample rate yourself.
  • Low-latency compressed streamingopus.
  • Something that must open in any audio editorwav.

Text handling

  • SSML is not supported. <speak>, <break/> and similar tags are read aloud as literal words, as are HTML entities like &amp;. Strip markup before sending.
  • All input is accepted. Emoji, CJK, Arabic, Cyrillic, accented Latin, URLs, tabs, newlines and control characters never produce an error.
  • Empty text succeeds, returning roughly 621 bytes of silent MP3. Validate on your side if that isn’t what you want.

Determinism

Output is non-deterministic. The same text, voice and response_format produce different audio — and a different byte count — on every call, even at a fixed temperature.
Don’t hash or byte-diff output to test correctness. Assert on successful decode, or on duration being within an expected range, instead.

See also

  • Streaming TTS — same request body, first audio in under 200 ms.
  • Voices — available speaker ids.
  • Errors — every status code and body shape.
  • For AI agents — the whole contract on one page.

Authorizations

x-api-key
string
header
required

Body

application/json
text
string
required
voice
string
required
normalize_text
boolean
default:false
response_format
AudioResponseFormat · object
temperature
number | null
timestamps
boolean
default:false

Response

Successful Response

audio
string
required
timestamps
SyncTimestamps · object | null