Create Custom Voice

June 5, 2026

Table of contents

  1. Request Headers
  2. Request Body
  3. Responses
  4. Model
  5. Examples
  6. Try It

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:

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"
}
  • email is required, the Google Flow account to create the voice on.
  • voice is 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
    
  • displayName is required, user-facing label for the voice. Range: 1-200 chars. Shown in GET /voices and used to distinguish multiple voices in your list.
  • dialog is required, the text the voice will say. Range: 1-120 chars. Used as the audio preview sample.
  • voicePerformance is required, short description of delivery style (e.g. “Cheerful, energetic delivery”, “Deep, brooding villain tone”). Range: 1-120 chars.
  • captchaToken, captchaRetry, captchaOrder are optional, mutually exclusive captcha parameters. See Captcha Parameters for details.

Responses

  • 200 OK

    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 voice field as the value for referenceAudio_1..referenceAudio_5 on POST /videos, or as the voice body param when creating a character via POST /characters. It’s also the path segment for GET /voices/ref and DELETE /voices/ref.

  • 400 Bad Request

    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,…)"
    }
    
  • 401 Unauthorized

    Invalid API token.

    { "error": "Unauthorized" }
    
  • 403 Forbidden

    Request rejected by Google — the reCAPTCHA token was outright rejected (not just scored low). After internal retries with fresh tokens, the worker returns this. The top-level error field carries a captcha_quality: prefix so tooling can detect this failure class. Increase captchaRetry (default 3) in the request body to 5 or higher, and configure additional providers via POST /accounts/captcha-providers so captchaOrder can cycle across them. If issues persist, contact your reCAPTCHA provider(s) support.

  • 404 Not Found

    Account not configured.

    { "error": "Google Flow account [email protected] not found" }
    
  • 429 Too Many Requests

    Google returns several RESOURCE_EXHAUSTED 429 variants. Check the reason field and respond accordingly:

    Reason Meaning Scope What to do Cooldown
    PUBLIC_ERROR_
    USER_REQUESTS_THROTTLED
    Per-user concurrency limit (the token was fine) All models on the account Run fewer parallel requests, retry later ~30 min
    PUBLIC_ERROR_
    USER_QUOTA_REACHED
    Account’s overall Flow quota cap All models on the account Add accounts, or omit email to load-balance ~30 min
    PUBLIC_ERROR_
    PER_MODEL_DAILY_QUOTA_REACHED
    Per-model daily quota Only that model, on that account Switch model, or wait Next UTC midnight
    PUBLIC_ERROR_
    UNUSUAL_ACTIVITY_TOO_MUCH_TRAFFIC
    Captcha-provider issue — low reCAPTCHA score (worker already auto-retried) Per request Add more captcha providers + captchaOrder, or raise captchaRetry 60s

    Every 429 returns the cooldown two ways — an HTTP Retry-After: <seconds> header and a body retryAfter: <ISO timestamp> field. Honor it before retrying.

    When email is omitted, the first three reasons quarantine the account and the load balancer routes around it. If every account is quarantined for the model you get 429 with error: "no_eligible_account". Routing details: GET /jobs › Load Balancing Algorithm.

    Captcha-quality failures prefix the top-level error with captcha_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.

  • 503 Service Unavailable

    503 has 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 error is prefixed Captcha 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'])
    

Try It