Grok Voice API Guide: Text to Speech Endpoint, Voices, Formats and Code

How to call the xAI Grok text to speech API: POST /v1/tts request body, the 28 voice ids, 20 languages, speech tags, output formats, timestamps, pronunciation fixes, pricing and working curl, Python and JavaScript examples.

Last updated:

This is a practical guide to the text to speech part of the xAI Grok Voice API: the one endpoint, every parameter that matters, the voice and language lists, and copy-paste code. For the conversational speech-to-speech model see Grok Voice 3.0; for agents see the voice agent guide. Everything below was checked against docs.x.ai on 16 October 2026.

The endpoint

POST https://api.x.ai/v1/tts
Authorization: Bearer $XAI_API_KEY
Content-Type: application/json

Minimal request:

curl -X POST https://api.x.ai/v1/tts \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello! Welcome to the xAI text to speech API.", "voice_id": "eve", "language": "en"}' \
  --output hello.mp3

The response body is the raw audio (MP3 unless you ask for another codec). There is no JSON wrapper unless you request timestamps.

Get a key at console.x.ai → API keys. Want to hear a voice before writing code? The free tool on this site calls the same endpoint.

Request body

FieldRequiredWhat it does
textyesUp to 60,000 characters. May contain speech tags.
languageyesBCP-47 code (en, zh, pt-BR, …) or auto. Case-insensitive.
voice_idnoOne of the 28 built-in ids (default eve) or a custom voice id. Case-insensitive.
speedno0.7 to 1.5; 1.0 is normal.
output_formatno{ "codec", "sample_rate", "bit_rate" }; default MP3, 24000 Hz, 128000 bps.
optimize_streaming_latencyno0 (best quality, default), 1 or 2 (smaller first chunk, faster first audio).
text_normalizationnotrue reads "$5" as "five dollars", expands abbreviations and numbers. Default false.
with_timestampsnotrue returns JSON with base64 audio plus per-character start/end times.
replacenoMap of phrases to how they should be spoken, as respellings or IPA between slashes. Up to 200 entries.

Voices

28 built-in ids, all lower-case, all able to speak every supported language:

ara eve leo rex sal altair atlas aurora carina castor celeste cosmo helios helix iris kepler liora lumen luna lux naksh orion perseus rigel sirius ursa zagan zenith

List them programmatically with GET /v1/tts/voices, or fetch one with GET /v1/tts/voices/{voice_id}. Each has a page with a live demo on this site: see all voices. Custom voices from the cloning API are passed in the same voice_id field.

Languages

auto, en, ar-EG, ar-SA, ar-AE, bn, zh, fr, de, hi, id, it, ja, ko, pt-BR, pt-PT, ru, es-MX, es-ES, tr, vi. xAI says the model can also produce other languages "with varying degrees of accuracy", but only these are validated. Set the code explicitly for mixed-language text; auto is fine for single-language input.

Speech tags

Two kinds of markers inside text:

  • Inline, a sound at that spot: [pause], [long-pause], [laugh], [chuckle], [giggle], [cry], [sigh], [breath], [inhale], [exhale], [hum-tune], [tsk], [tongue-click], [lip-smack].
  • Wrapping, a delivery change for the wrapped phrase: <whisper>, <soft>, <loud>, <build-intensity>, <decrease-intensity>, <higher-pitch>, <lower-pitch>, <slow>, <fast>, <emphasis>, <sing-song>, <singing>.
{
  "text": "So I walked in and [pause] there it was. [laugh] <whisper>It was a secret the whole time.</whisper>",
  "voice_id": "ara",
  "language": "en"
}

Tags nest (<slow><soft>…</soft></slow>) and are billed as characters but never spoken. All 26 tags, grouped the way xAI groups them and with an example sentence for each, are in the Grok TTS speech tags guide.

Output formats

codecContent-TypeUse
mp3audio/mpegDefault; web and apps
wavaudio/wavLossless; editing
pcmaudio/pcmRaw samples; real-time pipelines
mulawaudio/basicTelephony (G.711 μ-law)
alawaudio/alawTelephony (G.711 A-law)

sample_rate: 8000, 16000, 22050, 24000 (default), 44100 or 48000. bit_rate (MP3 only): 32000, 64000, 96000, 128000 (default) or 192000.

{
  "text": "Crystal clear.",
  "voice_id": "rex",
  "language": "en",
  "output_format": { "codec": "mp3", "sample_rate": 44100, "bit_rate": 192000 }
}

For a phone system: { "codec": "mulaw", "sample_rate": 8000 }.

Character-level timestamps

Set "with_timestamps": true and the response becomes JSON:

{
  "audio": "<base64 audio>",
  "content_type": "audio/mpeg",
  "duration": 0.92,
  "audio_timestamps": {
    "graph_chars": ["H", "e", "l", "l", "o"],
    "graph_times": [
      [0.0, 0.06],
      [0.06, 0.12],
      [0.12, 0.18],
      [0.18, 0.24],
      [0.24, 0.34]
    ]
  }
}

graph_chars mirrors your input character for character (spaces, punctuation and tags included) and graph_times is the parallel list of [start, end] seconds. Use it for captions, karaoke-style highlighting or lip sync. Walk the arrays in order rather than slicing your original string by index, because with text_normalization one written token ("$5") can be spoken as several words.

Pronunciation fixes with replace

{
  "text": "Deploy it behind nginx on Acme Mobile.",
  "voice_id": "leo",
  "language": "en",
  "replace": { "nginx": "/ˈɛndʒɪn ˈɛks/", "Acme Mobile": "Acme Mobull" }
}

Matching is case-insensitive on whole words, longest match wins, and you are still billed for the original text.

Code examples

Python

import os, requests

r = requests.post(
    "https://api.x.ai/v1/tts",
    headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
    json={"text": "Hello from Grok.", "voice_id": "ara", "language": "en", "speed": 1.1},
)
r.raise_for_status()
open("hello.mp3", "wb").write(r.content)

JavaScript (Node 18+ or any runtime with fetch)

const res = await fetch('https://api.x.ai/v1/tts', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.XAI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    text: 'Hello from Grok.',
    voice_id: 'ara',
    language: 'en',
  }),
});
if (!res.ok) throw new Error(`TTS error ${res.status}`);
await fs.promises.writeFile('hello.mp3', Buffer.from(await res.arrayBuffer()));

Keep the API key on a server. In a browser app, proxy the call through your backend (that is what this site does) or, for speech-to-speech sessions, mint an ephemeral client secret.

Pricing

$15.00 per million characters of input, counted on the text you send including tags and replace keys. A 1,000-character paragraph is $0.015. The other Voice API prices (speech to speech $0.08 per minute, speech to text $0.10 per hour) and a calculator are on the pricing page.

Errors you will meet

  • 401 – missing or wrong key.
  • 400 / 422 – unknown voice_id or language, speed out of 0.7–1.5, or malformed tags.
  • 413 – text over 60,000 characters; split it.
  • 429 – rate limit; back off and retry, or ask xAI for a higher limit.

Questions