# Gemini 3.1 Flash TTS (audio) > Text-to-speech on Google Gemini 3.1 Flash TTS, with 30 preset voices and 87 languages. It is best at a voice-over that you direct in plain words: tone, pace, accent, and emotion. It is the lowest per-character voice price in the catalog. Status: roasOS API v1.1. Catalog `model-catalog@2026-10-06.v5`, pricing `customer-pricing@2026-11-05.v15`. The endpoint `POST /v1/audio/gemini-3.1-flash-tts` is live. `POST /v1/generations` stays for one minor version as a deprecated alias (see "Today"). ## Endpoint POST https://api.roasos.com/v1/audio/gemini-3.1-flash-tts Auth: Authorization: Bearer roasos_sk_… (scope api:write). Idempotency-Key required. quote_only / quote_id / webhook_url as in https://docs.roasos.com/llms.txt. ## Good at A voice-over or a hook line that must sound a specific way. Write the delivery in `style_instruction` ("Warm and unhurried, like a friend recommending something") and keep `text` for the spoken words only. Inline audio tags such as `[sigh]`, `[laughing]`, `[whispering]`, and `[short pause]` steer single moments. Two voices can speak in one take: send `speakers` with two `{ speaker_id, voice }` entries and start every line of `text` with the alias and a colon (`Host: ...`). `voice` takes one of the 30 preset names; `Kore` is the default. `language` takes a BCP-47 code such as `en-US` or `fr-FR`; omit it and the model detects the language. It does not clone a voice and does not accept media of any kind. ## Pricing Unit: characters. Pricing key: `gemini-3.1-flash-tts`. | request | credits | |---|---| | `1000_characters` | 1 | | `2000_characters` | 2 | | `5000_characters` | 3 | Credits come from the roasOS pricing table (`MODEL_COSTS`) through the catalog. No other field changes the price. ## Modes - `text-to-speech` Provider endpoints: `fal-ai/gemini-3.1-flash-tts`. ## Request fields | field | type | required | default | rule | |---|---|---|---|---| | `language` | enum | no | — | `ar-EG`, `bn-BD`, `nl-NL`, `en-IN`, `en-US`, `fr-FR`, `de-DE`, `hi-IN`, `id-ID`, `it-IT`, `ja-JP`, `ko-KR`, `mr-IN`, `pl-PL`, `pt-BR`, `ro-RO`, `ru-RU`, `es-ES`, `ta-IN`, `te-IN`, `th-TH`, `tr-TR`, `uk-UA`, `vi-VN`, `af-ZA`, `sq-AL`, `am-ET`, `ar-001`, `hy-AM`, `az-AZ`, `eu-ES`, `be-BY`, `bg-BG`, `my-MM`, `ca-ES`, `ceb-PH`, `cmn-CN`, `cmn-TW`, `hr-HR`, `cs-CZ`, `da-DK`, `en-AU`, `en-GB`, `et-EE`, `fil-PH`, `fi-FI`, `fr-CA`, `gl-ES`, `ka-GE`, `el-GR`, `gu-IN`, `ht-HT`, `he-IL`, `hu-HU`, `is-IS`, `jv-JV`, `kn-IN`, `kok-IN`, `lo-LA`, `la-VA`, `lv-LV`, `lt-LT`, `lb-LU`, `mk-MK`, `mai-IN`, `mg-MG`, `ms-MY`, `ml-IN`, `mn-MN`, `ne-NP`, `nb-NO`, `nn-NO`, `or-IN`, `ps-AF`, `fa-IR`, `pt-PT`, `pa-IN`, `sr-RS`, `sd-IN`, `si-LK`, `sk-SK`, `sl-SI`, `es-419`, `es-MX`, `sw-KE`, `sv-SE`, `ur-PK` | | `output_format` | enum | no | `mp3` | `mp3`, `wav`, `ogg_opus` | | `quote_id` | string | no | — | UUID | | `quote_only` | boolean | no | `false` | — | | `speakers` | object[] | no | — | 1–2 items; each `{ speaker_id, voice }` | | `style_instruction` | string | no | — | ≥ 1 character; ≤ 2,000 characters | | `temperature` | number | no | `1` | 0–2 | | `text` | string | yes | — | ≥ 1 character; ≤ 5,000 characters | | `voice` | enum | no | `Kore` | `Kore`, `Puck`, `Charon`, `Zephyr`, `Aoede`, `Fenrir`, `Leda`, `Orus`, `Callirrhoe`, `Autonoe`, `Enceladus`, `Iapetus`, `Umbriel`, `Algieba`, `Despina`, `Erinome`, `Algenib`, `Rasalgethi`, `Laomedeia`, `Achernar`, `Alnilam`, `Schedar`, `Gacrux`, `Pulcherrima`, `Achird`, `Zubenelgenubi`, `Vindemiatrix`, `Sadachbia`, `Sadaltager`, `Sulafat` | | `wait_timeout_seconds` | integer | no | `0` | 0–60 | | `webhook_url` | string | no | — | https URL; ≤ 4,096 characters | The schema is strict: a field that is not in this table returns `400 unsupported_option`. Field names are the roasOS role names, one name per role across every family. ## Media The endpoint accepts no media. Every media role below is refused. - Refused: `avatar_id`, `elements`, `product_id`, `product_images`, `references`, `reference_images`, `image_url`, `end_image_url`, `scene_id`, `scene_image`, `video_urls`, `video_url`, `audio_urls`, `audio_url`, `voice_id`. A refused role returns `400 unsupported_option`. - Every URL must be an immutable object on `https://cdn.roasos.com/…` that your workspace owns; any other URL is `400 reference_media_not_workspace_owned`. Upload with `POST /v1/uploads` (direct POST of the bytes to `put_url`, or a `source_url` import), then use the returned CDN URL. ## Prompt `text` limit 5,000 characters; `style_instruction` limit 2,000. roasOS adds no wrapper. - Put the delivery in `style_instruction`, not in `text`. The model speaks every word of `text`. - Use one audio tag right before the words it changes. Too many tags make the read unstable. - For two voices, make each alias match a `speaker_id` exactly, and start each line with it. - Set `language` when the text mixes languages or when the accent matters. - Choose the voice by its character (Kore firm, Puck upbeat, Charon informative, Zephyr bright, Aoede breezy), then let the direction do the rest. - Lower `temperature` for a steady, repeatable read; raise it for a more varied one. ## Not exposed - `voice_id`: pinned to `the model has no provider voice-clone registry`. - `references`: not accepted by this public endpoint. - `avatar_id`: not accepted by this public endpoint. - `product_id`: not accepted by this public endpoint. - `audio_urls`: not accepted by this public endpoint. - `image_url`: not accepted by this public endpoint. - `speed`: not accepted by this public endpoint. - `pitch`: not accepted by this public endpoint. - `emotion`: not accepted by this public endpoint. ## Result Result kind `audio`. Always present: `url`. Present when the run produces it: `timestamps`. ```json { "data": { "id": "gen_…", "kind": "audio", "model": "gemini-3.1-flash-tts", "status": "completed", "result": {"timestamps":[{}],"url":"https://cdn.roasos.com/workspaces/ws_01/result"} } } ``` Files live on `https://cdn.roasos.com` and are immutable; a result URL can be a media input to the next call. Poll `GET /v1/jobs/{id}/status` → `{ id, status, updated_at }`, then `GET /v1/jobs/{id}/result` (`409 run_not_completed` until the job is `completed`, `failed` or `canceled`). Statuses: `queued → processing → completed | failed | canceled`. ## Example ```bash # 1. quote — nothing is spent curl -X POST https://api.roasos.com/v1/audio/gemini-3.1-flash-tts \ -H "Authorization: Bearer roasos_sk_live_…" \ -H "Idempotency-Key: 0f1c9e2a-gemini-3.1-flash-tts-0001" \ -H "Content-Type: application/json" \ -d '{"quote_only":true,"text":"Two drops, morning and night. That is the whole routine."}' # → 200 { "data": { "kind": "quote", "quote_id": "q_7Hk…", "credits": 1, "expires_at": "…", "confirmation": { "model": "gemini-3.1-flash-tts", "example": "1000_characters" } } } # 2. run — the same body and the same Idempotency-Key, quote_id instead of quote_only curl -X POST https://api.roasos.com/v1/audio/gemini-3.1-flash-tts \ -H "Authorization: Bearer roasos_sk_live_…" \ -H "Idempotency-Key: 0f1c9e2a-gemini-3.1-flash-tts-0001" \ -H "Content-Type: application/json" \ -d '{"quote_id":"q_7Hk…","text":"Two drops, morning and night. That is the whole routine."}' # → 201 { "data": { "id": "gen_…", "kind": "audio", "model": "gemini-3.1-flash-tts", "status": "queued", "credits_reserved": 1, "links": { "status": "/v1/jobs/gen_…/status", "result": "/v1/jobs/gen_…/result" } } } # 3. poll curl https://api.roasos.com/v1/jobs/gen_…/status -H "Authorization: Bearer roasos_sk_live_…" curl https://api.roasos.com/v1/jobs/gen_…/result -H "Authorization: Bearer roasos_sk_live_…" ``` ## Today `POST /v1/audio/gemini-3.1-flash-tts` is the endpoint. `POST /v1/generations` also works with `{ "type": "audio", "model": "gemini-3.1-flash-tts", "prompt": "", "options": { "voice": "Puck", "style_instruction": "Warm and amused.", "language": "en-GB" } }`; there the script goes in `prompt`. `options.speed`, `pitch`, `stability`, and `similarity_boost` have no provider field and are accepted at their default values only. ## See also https://docs.roasos.com/models/audio/gemini-3.1-flash-tts · https://api.roasos.com/v1/catalog/audio/gemini-3.1-flash-tts · fal-ai/gemini-3.1-flash-tts