VOICE / fix-pronunciation-with-tts-replace

Voice

Fix pronunciation with the TTS replace map

Fix pronunciation with the TTS replace map

Pass replace on POST https://api.x.ai/v1/tts (or session.update on the TTS WebSocket / Speech to Speech session) to swap how specific phrases are spoken without changing the text you send or the transcript the listener sees. You are still billed on the original characters.

Respellings and IPA

Values can be respellings or IPA phonetics. Keys stay the ordinary spelling you want to catch.

import os, requests

response = requests.post(
    "https://api.x.ai/v1/tts",
    headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
    json={
        "text": "nginx is returning errors on the Acme Mobile edge nodes.",
        "voice_id": "eve",
        "language": "en",
        "replace": {
            "nginx": "/ˈɛndʒɪn ˈɛks/",
            "Acme Mobile": "Acme Mobull",
        },
    },
)
response.raise_for_status()
open("spoken.mp3", "wb").write(response.content)

On the streaming TTS socket, send the map before the first text.delta:

{"type": "session.update", "replace": {"Acme Mobile": "Acme Mobull"}}
{"type": "text.delta", "delta": "Welcome to Acme Mobile."}
{"type": "text.done"}

The server replies session.updated with the map now in effect. An update mid-utterance applies to the next utterance.

Matching rules

  • Case-insensitive match; spoken casing follows the value you set.
  • Whole-word boundaries in space-separated scripts (Acme Mobiles does not match Acme Mobile).
  • Longest key wins when prefixes overlap.
  • Map limits: 200 entries; keys ≤ 100 chars; values ≤ 128 chars; keys are letters, digits, apostrophes, spaces only.
  • After substitution the spoken text must stay under 60,000 characters or the request fails (WebSocket sessions end).

Pitfalls

  • Broken map entries return 400 before any audio is generated.
  • With with_timestamps: true, graph_chars describes the spoken (replaced) text.
  • IPA typos produce confident wrong audio. Listen once before shipping a new entry.
  • Skip entries the model already says correctly (IEEE → "I triple E" unprompted).
  • Never expose the API key in the browser.