VOICE / get-tts-character-timestamps

Voice

Get character timestamps from Text to Speech

Get character timestamps from Text to Speech

Set with_timestamps: true on POST https://api.x.ai/v1/tts when you need captions, karaoke highlights, or lip-sync. The response switches from raw audio bytes to a JSON envelope with base64 audio plus per-character timing.

Request

import base64, os, requests

response = requests.post(
    "https://api.x.ai/v1/tts",
    headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
    json={
        "text": "Hello world.",
        "voice_id": "eve",
        "language": "en",
        "with_timestamps": True,
    },
)
response.raise_for_status()
payload = response.json()

open("hello.mp3", "wb").write(base64.b64decode(payload["audio"]))
ts = payload["audio_timestamps"]
for char, (start, end) in zip(ts["graph_chars"], ts["graph_times"]):
    print(f"{char!r:>5}  {start:.2f}s – {end:.2f}s")
print(f"duration: {payload['duration']:.2f}s")

Response shape

Field Meaning
audio Base64 audio in the requested codec
content_type MIME type (e.g. audio/mpeg)
duration Total seconds
audio_timestamps.graph_chars Each input character in order (spaces, punctuation, speech tags)
audio_timestamps.graph_times Parallel [start, end] pairs in seconds

graph_chars[i] lines up with graph_times[i]. Step the arrays together; do not slice the original string by byte index when text_normalization expands symbols ($5 stays two characters while spoken as "five dollars").

Streaming

Open wss://api.x.ai/v1/tts?…&with_timestamps=true. Each audio.delta can carry audio_timestamps and audio_duration for the characters in that chunk.

Pitfalls

  • Timestamps add a post-synthesis alignment pass, so latency goes up.
  • With a replace map, graph_chars follows the spoken (replaced) text.
  • Decode audio yourself; do not treat the JSON body as a playable file.
  • Never call TTS from the browser with the long-lived API key.
  • Console API credits are separate from SuperGrok's weekly pool on grok.com.