VOICE / start-speech-to-speech

Voice

Start a speech-to-speech session

Start a speech-to-speech session

Open wss://api.x.ai/v1/realtime?model=grok-voice-latest with a Bearer token. Send session.update, then conversation.item.create and response.create. Audio and text stream back as events. Region is us-east-1. Sessions last up to 120 minutes.

grok-voice-latest currently aliases grok-voice-think-fast-2.0. Pin a versioned name in production.

First turn

import asyncio
import json
import os
import websockets

async def voice_agent():
    async with websockets.connect(
        "wss://api.x.ai/v1/realtime?model=grok-voice-latest",
        additional_headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"}
    ) as ws:
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "voice": "eve",
                "instructions": "You are a helpful assistant.",
                "turn_detection": {"type": "server_vad"}
            }
        }))
        await ws.send(json.dumps({
            "type": "conversation.item.create",
            "item": {
                "type": "message",
                "role": "user",
                "content": [{"type": "input_text", "text": "Hello!"}]
            }
        }))
        await ws.send(json.dumps({"type": "response.create"}))
        async for msg in ws:
            event = json.loads(msg)
            print(event["type"])

asyncio.run(voice_agent())

With turn_detection.type set to server_vad, send mic bytes on input_audio_buffer.append and the server cuts turns. Default audio is PCM16 at 24 kHz, JSON-wrapped as base64 on response.output_audio.delta.

Browsers cannot set an Authorization header on WebSocket. Mint an ephemeral token server-side and open with protocol xai-client-secret.<token>. Keep the API key off the client.

session.tools can include web_search, x_search, file_search, mcp, and client function tools. Server-side tools run on xAI. Custom functions need you to handle response.function_call_arguments.done, send function_call_output, then response.create.

Pitfalls

  • Pass the API key in Authorization only from a server. Use ephemeral tokens in the browser.
  • conversation.item.create with text is billed as a text input message. function_call_output is not. Audio is billed by duration.
  • response.create is not a billable event. Generated audio on that turn is.
  • Max session is 120 minutes. Enable resumption.enabled and reconnect with ?conversation_id= if the socket drops. History expires after 30 minutes idle.
  • Console API credits are a separate bill from SuperGrok's weekly pool on grok.com.