Text to speech
Send text, receive audio. Compatible with the OpenAI speech API, with a few extra controls.
/v1/audio/speechThe response body is raw audio bytes, not JSON. Write it straight to a file or pipe it to a player. Audio starts arriving before the whole clip is rendered, so playback can begin early.
curl https://api.xkiro.com/v1/audio/speech \
-H "Authorization: Bearer $XKIRO_API_KEY" \
-H "Content-Type: application/json" \
-o speech.mp3 \
-d '{
"model": "xkiro-voice",
"input": "The quick brown fox jumps over the lazy dog.",
"voice": "mexican-female",
"response_format": "mp3"
}'Request body#
| Field | Type | Description |
|---|---|---|
modelrequired | string | A speech model ID — currently xkiro-voice. |
inputrequired | string | The text to speak. Billing is per character of this field, so trim boilerplate. |
voice | string | Voice ID. See Voices below. Omit for the model default. |
response_format | string | mp3 (default), opus, aac, flac, wav or pcm. Support varies by model; unsupported values return a clear 400. |
speed | number | 0.25–4.0. 1.0 is the natural rate. |
| Field | Type | Description |
|---|---|---|
pitch | number | Shifts the voice higher or lower. Ignored by models that cannot change pitch. |
volume | number | Output gain. |
emotion | string | Delivery style, e.g. 'happy', 'sad', 'angry'. Available values depend on the voice. |
stream | boolean | Send audio in chunks as it is produced, for a lower time to first byte. The bytes are the same either way. |
Unknown fields are ignored, not rejected
A strict OpenAI client can send this request unchanged, and an xKiro-aware client can add the extensions above. Neither breaks the other.
Voices#
/v1/audio/voicesPublic, cached for five minutes. Filter by `locale`, `languageKey`, `gender`, `isVip` or a free-text `q`.
curl "https://api.xkiro.com/v1/audio/voices?gender=female&limit=20"{
"total": 145,
"returned": 145,
"voices": [
{
"id": "mexican-female",
"name": "Mexican Female",
"locale": null,
"languageKey": null,
"gender": null,
"isVip": false,
"categories": []
},
{
"id": "female-voice-german",
"name": "Female Voice (German)",
"locale": null,
"languageKey": null,
"gender": null,
"isVip": false,
"categories": []
}
]
}Billing#
- Speech is billed per character of input, not per token.
- Your plan's daily allowance covers a number of characters; past that, usage is charged to your wallet. See Pricing & billing.
- A failed request is not charged. A request you cancel part-way is charged for the audio already produced.
Concurrency limit#
Speech is synchronous and holds an upstream connection for its whole duration, so each account has a cap on simultaneous speech requests — a separate limit from requests per minute. Exceeding it returns 429; wait for one to finish rather than retrying immediately.
Practical notes
- Split long text into paragraphs and request them separately. You get audio sooner and a failure costs you one paragraph, not the whole chapter.
- Cache generated audio keyed by the exact input, voice and settings. Speech for unchanged text is the easiest cost to eliminate.
- Punctuation drives pacing. Adding commas and full stops does more for naturalness than adjusting
speed.
