Mint an ephemeral token for Voice
Mint an ephemeral token for Voice
Mint a short-lived token on your server, pass it to the client, then open wss://api.x.ai/v1/realtime?model=grok-voice-latest. The long-lived API key stays on the server.
POST https://api.x.ai/v1/realtime/client_secrets with a Bearer API key. Example body: { "expires_after": { "seconds": 300 } }. The endpoint does not accept session or expires_after.anchor.
Mint on the server
FastAPI:
import os
import httpx
from fastapi import FastAPI
app = FastAPI()
@app.post("/session")
async def get_ephemeral_token():
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.x.ai/v1/realtime/client_secrets",
headers={
"Authorization": f"Bearer {os.environ['XAI_API_KEY']}",
"Content-Type": "application/json",
},
json={"expires_after": {"seconds": 300}},
)
return response.json()
Express:
app.post("/session", async (req, res) => {
const r = await fetch("https://api.x.ai/v1/realtime/client_secrets", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.XAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ expires_after: { seconds: 300 } }),
});
res.json(await r.json());
});
Hand the token from that JSON to the client.
Open the socket
Node and other servers can send the ephemeral token as Authorization: Bearer. Browsers cannot set that header on WebSocket. Open with protocol xai-client-secret.<token>:
const url = "wss://api.x.ai/v1/realtime?model=grok-voice-latest";
const ws = new WebSocket(url, [`xai-client-secret.${token}`]);
Pitfalls
- Never put
XAI_API_KEYin client code. Mint on the server; send only the ephemeral token. - Do not send
sessionorexpires_after.anchoron the mint request. - Console API credits are a separate bill from SuperGrok's weekly pool on grok.com.