Verified 2026-10-12 with a paid Gemini API key.
Request (single speaker, several items each with its own style works in one call):
curl -X POST https://generativelanguage.googleapis.com/v1beta/interactions \
-H "x-goog-api-key: $GEMINI_API_KEY" -H 'Content-Type: application/json' -d '{
"model": "gemini-3.8-flash-tts",
"input": [{"type": "user_input", "content": [
{"type": "text", "text": "First paragraph.", "annotations": [{"type": "speech_metadata", "style": "warm"}]},
{"type": "text", "text": "Second one. <short pause> Hm.", "annotations": [{"type": "speech_metadata", "style": "skeptical, wry"}]}]}],
"response_format": {"type": "audio"},
"generation_config": {"speech_config": [{"voice": "en-au-assistant-2"}]}}'Audio is base64 in steps[] | select(.type=="model_output") | .content[] | select(.type=="audio") | .data (take the last), mime audio/wav. Earlier TTS models returned headerless PCM; don't wrap 3.8 output in another WAV header.
Voices: GET /v1beta/voices?page_size=1000 + page_token -> 2,089 prebuilt voices in 30 locales. Each has id (e.g. en-gb-assistant-2, en-au-podcaster-5, or classic achird), accent (Winchester/Glasgow/Manchester/Newcastle/Bristol/Liverpool English, Sydney English, Dublin, Cape Town, Auckland, Indian, US regions...), gender, pitch, persona, context, description. Filters: accent, gender, region_code, persona, type, search.
Gotchas: text is spoken verbatim, so stage directions like 'Say cheerfully:' get read aloud; put delivery in speech_metadata.style and point events inline (<short pause>, <sigh>). Docs say not to put accent/age/gender changes in style: pick a regional voice. litellm documents only 2.5 TTS models; call REST directly for 3.8. Pricing on the Gemini pricing page: Flash TTS $9/1M audio tokens (25 tok/s, ~$0.0135/min) through 2026-12-31, Flash-Lite $6/1M. Measured: ~5 min of audio from 10 per-paragraph sequential calls in ~130 s; re-transcription word drift 0-4.4%, mostly ASR normalization.