Create Speech
Turn text into speech. Returns a durable WAV URL you can pass straight to video generation as `audio_url`.
Synchronous. The request follows the OpenAI speech shape (model, input, voice, instructions, response_format), but the response is JSON with a url, not raw audio bytes. The clip is stored on the CDN as a WAV (24 kHz, 16-bit mono) and registered as one of your audio clips, so it shows up in GET /v1/audio with source: "speech".
Three models. spicy-voice-1 speaks 45 preset voices. spicy-voice-1-expressive speaks 21 of them and also takes instructions describing emotion, pace, pitch and delivery. spicy-voice-1-custom speaks only your own voices, the vc_... ids from POST /v1/voices. A custom voice on a preset model, a preset voice on the custom model, or instructions on a model without them is a 400 that names the right model. GET /v1/voices lists every voice with the models that speak it.
Billed per 10,000 characters of input: $0.20 on spicy-voice-1, $0.23 on spicy-voice-1-expressive, $0.23 on spicy-voice-1-custom. CJK ideographs count as 2 characters. instructions are included in the debit; when the billed count comes back lower the difference is refunded, and cost_usd is the settled amount. Failed generations are refunded.
The input and the instructions are screened like a prompt before anything is charged; a blocked request returns the same 422 as a blocked prompt. dry_run: true validates, screens and returns the price without generating. Sandbox keys get a fixture clip flagged sandbox: true with the cost_usd a live key would pay; nothing is billed.
Make a character speak: pass the returned url byte-for-byte as audio_url on POST /v1/videos/generations. On spicy-motion-2, spicy-video-1 and the spicy-pov-* fine-tunes it is driving audio: the clip becomes the soundtrack and the mouth follows the words, alongside a first frame where the model takes one. On spicy-motion-3 and spicy-motion-3-fast it is reference audio: the model uses it for voice, tone and beat while the words come from the prompt, with a text prompt or references and never with a first frame (up to 5 clips, 15 seconds in total). Audio for video must be 2 to 30 seconds long, so split long text across requests.
curl --request POST \
--url https://api.spicyapi.com/v1/audio/speech \
--header 'Authorization: Bearer $SPICYAPI_KEY' \
--header 'Content-Type: application/json' \
--data '{
"model": "spicy-voice-1",
"input": "Come here. I have been waiting all evening.",
"voice": "Cherry"
}'{
"id": "au_7d2e9c1a4b6f8d0e2a",
"object": "audio.speech",
"model": "spicy-voice-1",
"url": "https://cdn.spicyapi.com/audio/a1b2c3d4e5f6a7b8/au_7d2e9c1a4b6f8d0e2a.wav",
"duration_s": 3.4,
"voice": "Cherry",
"cost_usd": 0.00086
}Body
application/jsonspicy-voice-1, spicy-voice-1-expressive or spicy-voice-1-custom.
The text to speak, at most 600 characters. Longer text is a 400: split it into several requests.
A preset voice name the model speaks (case-insensitive, e.g. Cherry), or on spicy-voice-1-custom one of your vc_... voice ids. See GET /v1/voices.
spicy-voice-1-expressive only: how to say it, in plain English or Chinese (emotion, pace, pitch, delivery), at most 1,600 characters. Counted in the debit, settled to the billed count.
One of Auto, English, Chinese, German, Italian, Portuguese, Spanish, Japanese, Korean, French, Russian (case-insensitive). Defaults to Auto, which detects the language as it goes.
wav, the default and only format. mp3 is a 400.
When true, validates and screens the request and returns { object: "dry_run", cost_usd, moderation } without generating or charging. A blocked prompt still returns the normal 422.
Your own id for the end user making this request (up to 128 characters, hashed at rest). Send it if your product serves many people: declined-prompt history, strikes and suspensions are then kept per end user, so one person's behaviour never affects another's requests or your account. After 10 severe violations that user gets 403 end_user_suspended; the id is echoed back as user in every screening error so you can act on it.
Response
200 · application/jsonSpeech
Clip id, au_...; the same id appears in GET /v1/audio.
Always audio.speech.
The model that spoke.
CDN URL of the WAV. Pass it byte-for-byte as audio_url (or in audio_urls) on POST /v1/videos/generations.
Length in seconds, to one decimal.
The preset name or vc_... id that was used.
What this request cost in US dollars, settled to the billed character count.
Was this page helpful?