Create Custom Voice
June 5, 2026
Table of contents
Create a custom voice (also called a user voice) on a Google Flow account. A custom voice is a saved Text-to-Speech (TTS) clip built on top of one of the 30 system voice presets (Achernar, Achird, Algenib, …) with your own dialog text and voicePerformance description.
Once created, the voice can be:
- Used in POST /videos via
referenceAudio_1..5 - Attached to a POST /characters at create time (
voicebody param) - Played back via GET /voices/
ref(returns a fresh signedaudioUrl)
Captcha: required. See Captcha Parameters.
https://api.useapi.net/v1/google-flow/voices
Request Headers
Authorization: Bearer {API token}
Content-Type: application/json
Request Body
{
"email": "[email protected]",
"voice": "Achernar",
"dialog": "Hello, this is a test voice.",
"voicePerformance": "Cheerful, energetic delivery",
"displayName": "Cheerful Narrator"
}
emailis required, the Google Flow account to create the voice on.-
voiceis required, one of the 30 system voice presets. The custom voice inherits the timbre of this base voice. Case-sensitive. Valid values:Achernar, Achird, Algenib, Algieba, Alnilam, Aoede, Autonoe, Callirrhoe, Charon, Despina, Enceladus, Erinome, Fenrir, Gacrux, Iapetus, Kore, Laomedeia, Leda, Orus, Puck, Pulcherrima, Rasalgethi, Sadachbia, Sadaltager, Schedar, Sulafat, Umbriel, Vindemiatrix, Zephyr, Zubenelgenubi displayNameis required, user-facing label for the voice. Range: 1-200 chars. Shown in GET /voices and used to distinguish multiple voices in your list.dialogis required, the text the voice will say. Range: 1-120 chars. Used as the audio preview sample.voicePerformanceis required, short description of delivery style (e.g. “Cheerful, energetic delivery”, “Deep, brooding villain tone”). Range: 1-120 chars.captchaToken,captchaRetry,captchaOrderare optional, mutually exclusive captcha parameters. See Captcha Parameters for details.
Responses
-
Voice created.
{ "voice": "user:12345-email:6a6f...-voice:d990a2f9-...-mid:d55b6d59-...", "source": "user", "workflowId": "d990a2f9-...", "mediaId": "d55b6d59-...", "displayName": "Cheerful Narrator", "baseVoice": "Achernar", "dialog": "Hello, this is a test voice.", "voicePerformance": "Cheerful, energetic delivery", "audioUrl": "https://flow-content.google/audio/d55b6d59-...?Expires=...&KeyName=labs-flow-prod-cdn-key&Signature=..." }Use the
voicefield as the value forreferenceAudio_1..referenceAudio_5on POST /videos, or as thevoicebody param when creating a character via POST /characters. It’s also the path segment for GET /voices/refand DELETE /voices/ref. -
Validation error (missing required field, invalid voice name, dialog/voicePerformance > 120 chars, etc).
{ "error": "Parameter voice is required (must be one of Achernar,Achird,Algenib,…)" } -
Invalid API token.
{ "error": "Unauthorized" } -
Google rejected the captcha on every try. We retry with a fresh captcha as many times as you allow (
captchaRetry, default 5, or the providers incaptchaOrder) and return this403only when all of them were rejected. The top-levelerrorstarts withcaptcha_quality:, for example"captcha_quality: PUBLIC_ERROR_UNUSUAL_ACTIVITY after 5 attempts — see GET /accounts/captcha-stats summary.by_account".One
403is not a captcha problem.PUBLIC_ERROR_MODEL_ACCESS_DENIEDin the response means Google refused the model on that account, so raisingcaptchaRetryor adding providers will not help. Check that the account’s plan includes the model. Since 2026-09-23 Google no longer offersveo-3.1-lite-low-priorityto invited members of an Ultra $199 family plan — only the family manager’s account keeps it.Nothing was created when this happens. Google checks the captcha before it starts generating, so no Google credits were used and you can resubmit straight away. Your captcha provider still bills each solve.
The usual cause is the Google account, not the captcha provider. Google gives each account its own trust level. On a trusted account it accepts about 9 in 10 captchas, on a distrusted one about 3 in 10, whichever provider solved them. Most customers have a few distrusted accounts among many good ones.
To see which accounts are affected, use GET /accounts/captcha-stats. Its
summary.by_accountshows, for each of your Google accounts, how many captchas were tried and what share Google accepted. GET account/stats withbot=google-flowalso shows how many requests each account finished with each response code.- If one or a few accounts are far below the others, the API already sends them less work: when you leave out
email, each captcha Google rejected on an account in the last 15 minutes lowers its chance of being picked, for generations and for image uploads. They still get some work, so an account that recovers gets its full share back. If you pin requests withemail, move them to your other accounts yourself. Replace an account that stays low for more than a day or two. - If all your accounts drop at the same time, it’s the captcha provider. Configure at least two providers with POST /accounts/captcha-providers, and the API will start with whichever one Google accepts best.
A higher
captchaRetrygets more requests through on a distrusted account, but every extra try is another paid solve. - If one or a few accounts are far below the others, the API already sends them less work: when you leave out
-
Account not configured.
{ "error": "Google Flow account [email protected] not found" } -
Google returns several
RESOURCE_EXHAUSTED429 variants. Check thereasonfield and respond accordingly:Reason Meaning Scope What to do Cooldown PUBLIC_ERROR_USER_REQUESTS_THROTTLEDGoogle is throttling the account (the token was fine). Uploads and generation are metered separately All models on the account — or uploads only, when POST /assets triggered it Run fewer parallel requests, or switch accounts. From POST /assets, retrying the same one rarely clears it ~30 min. From POST /assets, treat it as a minimum — it can persist PUBLIC_ERROR_USER_QUOTA_REACHEDAccount’s overall Flow quota cap All models on the account Add accounts, or omit emailto load-balance~30 min PUBLIC_ERROR_PER_MODEL_DAILY_QUOTA_REACHEDPer-model daily quota Only that model, on that account Switch model, or wait Next UTC midnight PUBLIC_ERROR_UNUSUAL_ACTIVITY_TOO_MUCH_TRAFFICGoogle rejected the captcha on every try (we already retried). Usually a distrusted Google account, sometimes the provider Per request Check which accounts are affected in captcha-stats. Rest or replace those accounts. Add a second provider if all accounts drop together 60s Every
429returns the cooldown two ways — an HTTPRetry-After: <seconds>header and a bodyretryAfter: <ISO timestamp>field. Honor it before retrying.When
emailis omitted, the first three reasons quarantine the account and the load balancer routes around it. If every account is quarantined for the model you get429witherror: "no_eligible_account". Routing details: GET /jobs › Load Balancing Algorithm.Captcha-quality failures prefix the top-level
errorwithcaptcha_quality:(e.g."captcha_quality: PUBLIC_ERROR_UNUSUAL_ACTIVITY_TOO_MUCH_TRAFFIC after 3 attempts") so you can detect them without parsing the nested body. -
503has two distinct causes — check the response body to tell them apart.A transient Google-side outage. Wait 5-10 seconds and retry.
{ "error": { "code": 503, "message": "Service temporarily unavailable.", "status": "UNAVAILABLE" } }A captcha-provider failure — the top-level
erroris prefixedCaptcha service failed:. The problem is your captcha provider (check its API key and balance), not Google. Don’t retry until the provider is back.{ "error": "Captcha service failed: ERROR_ZERO_BALANCE", "code": 503 }
Model
{
voice: string // reference-id used in subsequent calls
source: 'user'
workflowId: string
mediaId: string
displayName: string
baseVoice: string // one of the 30 system voice presets
dialog: string
voicePerformance: string
audioUrl?: string // signed playback URL (~6h TTL); occasionally absent on transient resolution failure
}
Examples
-
curl -X POST \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "[email protected]", "voice": "Achernar", "dialog": "Welcome to the demo.", "voicePerformance": "Cheerful, energetic delivery", "displayName": "Cheerful Narrator" }' \ "https://api.useapi.net/v1/google-flow/voices" -
const response = await fetch('https://api.useapi.net/v1/google-flow/voices', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ email: '[email protected]', voice: 'Achernar', dialog: 'Welcome to the demo.', voicePerformance: 'Cheerful, energetic delivery', displayName: 'Cheerful Narrator' }) }) const result = await response.json() console.log('voice ref:', result.voice) -
import requests r = requests.post( 'https://api.useapi.net/v1/google-flow/voices', headers={'Authorization': f'Bearer {token}'}, json={ 'email': '[email protected]', 'voice': 'Achernar', 'dialog': 'Welcome to the demo.', 'voicePerformance': 'Cheerful, energetic delivery', 'displayName': 'Cheerful Narrator', }, ) print('voice ref:', r.json()['voice'])