xAI (Grok API) — provider research for PolyLLM
Last documentation audit: 2026-09-08
Probes executed 2026-09-08 with a real key against https://api.x.ai/v1 (scripts in research/xai/, raw outputs in research/xai/out/). Everything marked (probed) was observed live; everything marked (docs) comes from the pages listed at the end.
Heads-up:
docs.x.ai/docs/...URLs given in the brief are all 404 now. The documentation moved tohttps://docs.x.ai/developers/...(see "Documentation pages used"). The API error for Live Search still points to the old/docs/guides/tools/overviewURL, which is dead.
1. Base URL, auth, headers
| Item | Value |
|---|---|
| REST base URL | https://api.x.ai/v1 (also a gRPC API at api.x.ai:443, not relevant for a web app) |
| Auth | Authorization: Bearer <XAI_API_KEY> |
| Content type | application/json (malformed JSON → 422 text/plain Rust/serde message, e.g. Failed to deserialize the JSON body into the target type: messages: invalid type: string "nope", expected a sequence at line 1 column 37) (probed) |
| Useful request headers | x-grok-conv-id: <stable id> — routes a conversation to the same server to maximise prompt-cache hits (docs); accepted (probed). Body field prompt_cache_key is the equivalent for Responses API. |
| Response headers (probed) | x-request-id, x-ratelimit-limit-requests, x-ratelimit-remaining-requests, x-ratelimit-limit-tokens, x-ratelimit-remaining-tokens (present on inference calls; not on /models). Behind Cloudflare (cf-ray). Observed limits: grok-4.6 7200 req / 50 000 000 tokens per window; grok-4.3 1800 req / 10 000 000 tokens (window is per minute per the TPM docs; the RPS numbers below suggest the header window is 1 minute). |
| Key introspection | GET /v1/api-key (redacted key, name, ACLs, team/user ids) and GET /v1/me (adds zdr_status, team_blocked) (probed, 200). Handy for a "validate key" button: cheap, no tokens. But an invalid key returns 400, not 401 (see §14). |
2. SDK recommendation (TypeScript / Node)
- There is no official xAI JavaScript/TypeScript SDK.
xai-sdkis Python-only (gRPC). npmxai-sdk/@xai/sdkdo not exist (checked 2026-09-08). - xAI's own docs give JS examples with
openai(OpenAI SDK) pointed atbaseURL: "https://api.x.ai/v1"and with Vercel AI SDK@ai-sdk/xai(latest 4.0.54;xai(model)= Responses API since AI SDK 7,xai.chat(model)= legacy chat completions; exposesxai.tools.webSearch/xSearch/codeExecution/mcpServer/…). - Anthropic SDK compatibility (
/v1/messages,/v1/complete) is "fully deprecated" (docs, Legacy section). Do not build on it. - Recommendation for PolyLLM: use the OpenAI SDK
openai@7.10.0(what we probed) withbaseURL: https://api.x.ai/v1,maxRetrieshandled by us, and a longtimeout(docs recommend 3600 s for reasoning models; we used 120 s fine for small prompts). Bothclient.chat.completions.createandclient.responses.creatework unchanged. A rawfetchSSE parser also works (see §16) and is what we used to capture exact chunk shapes.
3. Endpoints (inference + management)
| Endpoint | Status | Notes |
|---|---|---|
POST /v1/responses |
preferred (docs: "The Responses API is the preferred way… New features come to the Responses API first") | Stateful by default (store: true, 30-day retention), previous_response_id, server-side tools, encrypted reasoning, structured outputs via text.format. (probed) |
GET /v1/responses/{id}, GET /v1/responses/{id}/input_items, DELETE /v1/responses/{id} |
active | Delete returns {"id","object":"response","deleted":true} (probed) |
POST /v1/responses/compact |
active | Context compaction → opaque blob to feed back verbatim (docs) |
POST /v1/chat/completions |
legacy but fully working | OpenAI-compatible; reasoning_content returned; no deprecation date. Multi-agent model rejected here. (probed) |
GET /v1/chat/deferred-completion/{request_id} |
active | For deferred: true requests (202 while pending, 24 h retention, single read) (docs) |
POST /v1/completions, POST /v1/messages, POST /v1/complete |
deprecated/legacy | Not supported by reasoning models; Anthropic compat fully deprecated (docs) |
GET /v1/models, GET /v1/models/{id} |
active | Minimal (id, aliases). (probed) |
GET /v1/language-models, GET /v1/language-models/{id} |
active | Rich metadata + pricing (§12). Does NOT include context_length despite docs example. (probed) |
GET /v1/image-generation-models, GET /v1/video-generation-models (+/{id}) |
active | Image: image_price, pricing[], max_prompt_length. (probed) |
POST /v1/tokenize-text |
active | {text, model} → {token_ids:[{token_id,string_token,token_bytes}]} (probed) — usable for client-side token counting (one network call). |
GET /v1/api-key, GET /v1/me |
active | See §1 |
POST /v1/images/generations, /v1/images/edits, videos, voice (STT/TTS/speech-to-speech), /v1/files, collections, batches, embeddings (section exists, no text-embedding model listed) |
active | Out of scope for the chat adapter except image generation (grok-imagine-*). |
4. Chat Completions request/response (probed shapes)
Request body fields (docs API ref + probes): model, messages, max_completion_tokens (default 128 000; max_tokens is deprecated but still accepted (probed)), temperature 0–2, top_p 0–1, n, seed, stop (≤4; rejected by reasoning models), frequency_penalty/presence_penalty (rejected by every current model), logit_bias, logprobs/top_logprobs (0–8; "ignored by grok-4.20+", accepted (probed)), reasoning_effort (none|low|medium|high|xhigh, model-dependent), response_format, tools (≤128 docs API ref; ≤200 docs function-calling page), tool_choice, parallel_tool_calls, stream, stream_options.include_usage, prompt_cache_key, service_tier (default|priority), user, deferred, web_search_options (OpenAI compat), search_parameters (dead → 410).
Roles: system, user, assistant, tool. developer role is accepted (probed, all 6 models). Unknown top-level params (foo_bar) are silently ignored (probed); top_k is accepted on chat completions although only documented for Responses (probed).
Non-streaming response (probed):
{
"id": "…", "object": "chat.completion", "created": 1788842609, "model": "grok-4.6",
"choices": [{ "index": 0, "finish_reason": "stop",
"message": { "role": "assistant", "content": "2 + 2 equals 4.",
"reasoning_content": "The user asked: …", "refusal": null } }],
"usage": {
"prompt_tokens": 652, "completion_tokens": 8, "total_tokens": 770,
"prompt_tokens_details": { "text_tokens": 652, "audio_tokens": 0, "image_tokens": 0, "cached_tokens": 512 },
"completion_tokens_details": { "reasoning_tokens": 110, "audio_tokens": 0,
"accepted_prediction_tokens": 0, "rejected_prediction_tokens": 0 },
"num_sources_used": 0, "cost_in_usd_ticks": 12440000 },
"system_fingerprint": "fp_e41c6060b2628547", "service_tier": "default"
}total_tokens= prompt + completion + reasoning (reasoning tokens are not insidecompletion_tokens; 652+8+110=770).cost_in_usd_ticks: 10 000 000 000 ticks = $1 (docs) → 12 440 000 ticks = $0.001244. Responses API also returnscost_in_usd_ticks(and docs mentioncost_in_nano_usd). Use this for exact per-request cost display.finish_reason:stop,length(probed:max_completion_tokens: 10→"length"on both reasoning and non-reasoning models; on reasoning models the cap applies to visible tokens, reasoning still ran ~680 tokens),tool_calls(probed). Docs also listend_turn(streaming).- Hidden system prompt: even a one-line prompt costs ~190–200 prompt tokens on grok-4.3/4.20/build, ~500 on grok-4.5 and ~650 on grok-4.6 (probed) — budget for it.
5. Streaming protocol (chat completions) (probed)
SSE, Content-Type: text/event-stream, no event: field, data: {json} lines, terminated by data: [DONE].
Chunk sequence for a reasoning model (grok-4.6):
data: {"id":"1039…","object":"chat.completion.chunk","created":1788842609,"model":"grok-4.6",
"choices":[{"index":0,"delta":{"reasoning_content":"The","role":"assistant"}}],
"system_fingerprint":"fp_e41c6060b2628547","service_tier":"default"}
data: {"…","choices":[{"index":0,"delta":{"reasoning_content":" user"}}],…}
…
data: {"…","choices":[{"index":0,"delta":{"content":"Bonjour"}}],…}
data: {"…","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],…}
data: {"…","choices":[],"usage":{"prompt_tokens":646,"completion_tokens":3,"total_tokens":1189,
"prompt_tokens_details":{…,"cached_tokens":512},
"completion_tokens_details":{"reasoning_tokens":540,…},"num_sources_used":0,"cost_in_usd_ticks":37820000},…}
data: [DONE]- Delta keys observed:
role,reasoning_content,content,tool_calls. Non-reasoning model (grok-4.20-non-reasoning): onlyrole/content. - Reasoning is streamed as
delta.reasoning_content(plain text) for grok-4.6, 4.5, 4.3, 4.20-reasoning, grok-build → PolyLLM can render a live "thinking" pane on chat completions without any extra flag. - Usage arrives in a final extra chunk with
choices: []only whenstream_options: {include_usage: true}; thefinish_reasonchunk is separate. Nox-ratelimitdifferences for streams. - Chunks carry no per-chunk
usage(docs' example showing usage on every chunk is outdated).
6. Tool / function calling (probed on all 6 models)
- Chat completions: OpenAI nested format
{type:"function", function:{name, description, parameters}};tool_choice:auto|required|none|{type:"function",function:{name}};parallel_tool_calls(default true). Parameter root must be an object (or anyOf/oneOf of objects). - Round trip works with
assistant.tool_calls+{role:"tool", tool_call_id, content}on every model. - Streaming: the whole tool call arrives in ONE chunk (docs + probed), with full
arguments:
{"delta":{"tool_calls":[{"id":"call-cbf4aac4-…-0","function":{"name":"get_weather","arguments":"{\"city\":\"Montreal\"}"},"index":0,"type":"function"}]}}then finish_reason: "tool_calls". The non-reasoning model also includes "role":"assistant" in that delta. Standard OpenAI accumulation code works, but you can also treat each tool_calls delta as complete. Tool-call ids look like call-<uuid>-<n>.
- Responses API: flat tool
{type:"function", name, description, parameters}; eventsresponse.output_item.added(itemtype:"function_call",call_id,name,arguments:""), oneresponse.function_call_arguments.deltawith the full JSON,response.function_call_arguments.done,response.output_item.done. Return results as{type:"function_call_output", call_id, output}items (probed). - Tool arguments are implicitly
strict(docs: "Tool calling… implicit strict: true").
7. Structured output (probed on all 6 models)
- Chat completions:
response_format: {type:"json_schema", json_schema:{name, schema, strict:true}}→ valid JSON on all 6 models;{type:"json_object"}also works on all 6 (docs say it is accepted). - Responses API:
text: {format: {type:"json_schema", name, schema, strict:true}}(probed OK). - OpenAI SDK helpers work:
client.chat.completions.parse(... response_format: zodResponseFormat(...))(docs). - JSON Schema support (docs): string/number/integer/boolean/null/enum/const/array/object/anyOf/oneOf/allOf(single)/$ref+$defs (non-circular); enforced formats date, time, date-time, email, uuid, ipv4, ipv6, uri; minLength/maxLength ≤2048, minItems/maxItems ≤256, min/maxProperties ≤64;
additionalPropertiesdefaults to false; rejected: empty enum/anyOf, boolean-valued properties, min/maxContains, tupleitems. Regex: no backrefs/lookaround/\b/Unicode properties;.matches newline;^/$implicit. "Structured Outputs are available on Grok 4 family models."
8. Reasoning controls (probed matrix)
| Model | Reasoning | reasoning_effort accepted |
reasoning_content returned |
|---|---|---|---|
| grok-4.6 | always on | low, medium, high (default), xhigh; minimal accepted (undocumented, likely mapped); none → 400 This model does not support \reasoning_effort` value `none`.` |
yes (chat completions, plain text; Responses: reasoning.summary[] + optional encrypted_content) |
| grok-4.5 | always on | low, medium, high (default); xhigh/minimal accepted (docs: xhigh treated as high); none → 400 same message |
yes |
| grok-4.3 | configurable | none accepted → reasoning_tokens 0, low, medium, high; xhigh/minimal accepted (mapped) |
yes (empty when none) |
| grok-4.20-0309-reasoning | always on, fixed | any value → 400 Model grok-4.20-0309-reasoning does not support parameter reasoningEffort. |
yes |
| grok-4.20-0309-non-reasoning | none | any value → 400 same pattern | no (reasoning_tokens: 0) |
| grok-build-0.1 | always on, fixed | any value → 400 same pattern | yes |
| grok-4.20-multi-agent-0309 | always on | `low | medium= 4 agents,high |
- Same behaviour via Responses API
reasoning: {effort}(probed): 4.20-*/build rejectlow; 4.3 acceptsnone(reasoning.summarybecomes"none", no reasoning item). - Responses API returns reasoning as an output item
{type:"reasoning", summary:[{type:"summary_text", text}], encrypted_content?}; streamed asresponse.reasoning_summary_part.added/response.reasoning_summary_text.delta/…done/response.reasoning_summary_part.done. Withinclude: ["reasoning.encrypted_content"]you get an opaqueencrypted_contentyou can pass back in theinputfor stateless multi-turn with reasoning continuity (docs).reasoning.summary(auto|concise|detailed) is "compatibility only"; the API echoed"summary":"detailed"when we sentauto. - No thinking-budget parameter exists. Reasoning tokens are billed at the output price.
9. Sampling & other parameters — support matrix (probed, chat completions)
| Param | 4.6 | 4.5 | 4.3 | 4.20-reasoning | 4.20-non-reasoning | build-0.1 |
|---|---|---|---|---|---|---|
temperature (0–2) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
top_p |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
top_k (undocumented on chat) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
seed (+ system_fingerprint) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
max_completion_tokens |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
max_tokens (deprecated) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
n: 2 |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
logprobs/top_logprobs |
✓ (accepted; docs: ignored on 4.20+) | ✓ | ✓ | ✓ | ✓ | ✓ |
response_format json_object / json_schema |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
stop |
400 | 400 | 400 | 400 | ✓ | 400 |
frequency_penalty |
400 | 400 | 400 | 400 | 400 | 400 |
presence_penalty |
400 | 400 | 400 | 400 | 400 | 400 |
reasoning_effort |
see §8 | 400 | 400 | 400 | ||
developer role |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Exact rejection text: {"code":"invalid-argument","error":"Model grok-4.6 does not support parameter presencePenalty."} (camelCase param names: presencePenalty, frequencyPenalty, stop, reasoningEffort). Adapter rule: never send frequency_penalty/presence_penalty to xAI; send stop only to grok-4.20-0309-non-reasoning; send reasoning_effort only to 4.6/4.5/4.3 (and multi-agent). On /v1/responses, presence_penalty was silently accepted and echoed as 0 (probed) — the Responses endpoint is more lenient; Responses defaults echoed: temperature 0.7, top_p 0.95, truncation "disabled".
10. Modalities, context, output limits
- Input: text + image on all 7 language models (
input_modalities: ["text","image"]from/v1/language-models; vision probed OK on all 6 chat models). No audio input on text models (separate voice models/endpoints). Output: text only. Image generation = separate modelsgrok-imagine-image,-2.0,-qualityvia/v1/images/generations(also animage_generationserver-side tool on Responses); video =grok-imagine-video,-1.5. - Images: JPG/PNG, ≤20 MiB, data URL
data:image/png;base64,…or public URL,detail: "high"accepted, unlimited count (docs). Hard minimums (probed, undocumented): width and height ≥ 8 px and total ≥ 512 pixels — errors{"code":"invalid_image","error":"Image dimensions 2x2 are too small. Both width and height must be at least 8 pixels."}/"Image has 64 total pixels (8x8), which is below the minimum of 512 pixels."/"Invalid PNG image.". A 32×32 PNG cost 1–3image_tokens. Image tokens billed at the text input price (prompt_image_token_price==prompt_text_token_price). - Chat completions image format:
{type:"image_url", image_url:{url, detail}}; Responses:{type:"input_image", image_url:"<url or data url>", detail}(string, not object) +{type:"input_text", text}. - Context windows (docs, not in API): grok-4.6 500k, grok-4.5 500k, grok-4.3 / 4.20-* / multi-agent 1M, grok-build-0.1 256k.
long_context_threshold= 200 000 tokens for every model: when the prompt exceeds it, input/cached/output are all billed at 2×. - Max output: docs — grok-4.6 "no text output limit"; API default
max_completion_tokens/max_output_tokens= 128 000.max_output_tokenson Responses includes reasoning tokens;max_completion_tokenson chat completions covers visible tokens only (docs + probed). - Files:
POST /v1/files(48 MB, text/PDF/code),input_filewithfile_id/file_urlon Responses, autoattachment_searchtool ($10/1k calls), agentic models only (4.20, 4.5, 4.6). Collections (RAG) $2.50/1k searches.
11. Server-side (agentic) tools, search, citations (probed on grok-4.3 via /v1/responses)
- Live Search (
search_parameters) is dead: chat completions → 410{"error":"Live search is deprecated. Please switch to the Agent Tools API: https://docs.x.ai/docs/guides/tools/overview"};tools:[{type:"live_search",sources:[…]}]→ 410 too;tools:[{type:"web_search"}]on chat completions → 422tools[0].type: unknown variant \web_search`, expected `function` or `live_search``. Server-side tools are Responses-API-only. - Responses tools:
{type:"web_search", allowed_domains?(≤5), excluded_domains?(≤5), enable_image_understanding?, enable_image_search?},{type:"x_search", allowed_x_handles?/excluded_x_handles? (≤20), from_date?, to_date?, enable_image_understanding?, enable_video_understanding?},{type:"code_interpreter"}(sandboxed Python),{type:"mcp", server_url, server_label, server_description?, allowed_tools?, authorization?, headers?}(Streamable HTTP/SSE),{type:"file_search"}/collections_search,{type:"image_generation"}.max_turnscaps agentic loops. Tool-call output is not returned by default; docs'includevalues (web_search_call_output, …) are rejected (400 Argument not supported: "web_search_call_output" in "include" field) — what is accepted:include: ["web_search_call.action.sources"]and["no_inline_citations"](probed). - Streaming event names observed with web_search:
response.created,response.in_progress,response.output_item.added,response.reasoning_summary_part.added,response.reasoning_summary_text.delta,response.reasoning_summary_text.done,response.reasoning_summary_part.done,response.output_item.done,response.web_search_call.in_progress,response.web_search_call.searching,response.web_search_call.completed,response.content_part.added,response.output_text.delta,response.output_text.annotation.added,response.output_text.done,response.content_part.done,response.completed. Each event hassequence_number,item_id,output_index. - Citations: (1) inline Markdown
[[N]](url)in the text (default on; disable withinclude:["no_inline_citations"]), (2)annotations: [{type:"url_citation", url, start_index, end_index, title:"1"}]on theoutput_textpart, (3)output[]items{type:"web_search_call", status, action:{type:"search", query, sources:[{type:"url", url}]}}(one per search, 3 searches for our question). A top-levelresponse.citationsfield was not present in the REST response (the docs'response.citationsis the Python SDK). Usage addsnum_server_side_tools_used,server_side_tool_usage_details:{web_search_calls, x_search_calls, code_interpreter_calls, file_search_calls, mcp_calls, document_search_calls, image_generation_calls}andcontext_details. Our single question consumed 20 919 input tokens (11 072 cached) + 856 output → ≈ $0.032 + 3 × $0.005 tool calls. Budget accordingly. - Tool pricing (docs): web/X search $5 per 1 000 calls, code execution $5/1k, file attachments $10/1k, collections $2.50/1k; image/video understanding token-based.
- No computer-use tool. Image generation available as a tool and as
/v1/images/generations.
12. Model listing & pricing units (probed)
GET /v1/language-models fields: id, fingerprint, created, object, owned_by, version, input_modalities, output_modalities, prompt_text_token_price, cached_prompt_text_token_price, prompt_image_token_price, completion_text_token_price, search_price, prompt_text_token_price_long_context, cached_prompt_text_token_price_long_context, completion_text_token_price_long_context, long_context_threshold, aliases. No context_length, no max output.
Unit (verified against the pricing page): USD cents per 100 000 000 tokens → $ per 1M tokens = value / 10 000. grok-4.6 prompt_text_token_price: 20000 → $2.00/M ✓; cached 5000 → $0.50 ✓; completion 60000 → $6.00 ✓; grok-4.3 12500 → $1.25 ✓. Image models: image_price in 1/100 000 000 cent → grok-imagine-image 200000000 = $0.02 ✓.
| Model | ctx (docs) | in / cached / out ($/M, <200k) | ≥200k tokens | aliases |
|---|---|---|---|---|
| grok-4.6 | 500k | 2.00 / 0.50 / 6.00 | 4.00 / 1.00 / 12.00 | — |
| grok-4.5 | 500k | 2.00 / 0.30 / 6.00 | 4.00 / 0.60 / 12.00 | grok-4.5-latest, grok-build-latest |
| grok-4.3 | 1M | 1.25 / 0.20 / 2.50 | 2.50 / 0.40 / 5.00 | grok-4.3-latest |
| grok-4.20-0309-reasoning | 1M | 1.25 / 0.20 / 2.50 | 2.50 / 0.40 / 5.00 | grok-4.20, grok-4.20-reasoning, … (15) |
| grok-4.20-0309-non-reasoning | 1M | 1.25 / 0.20 / 2.50 | 2.50 / 0.40 / 5.00 | grok-4.20-non-reasoning, … (8) |
| grok-4.20-multi-agent-0309 | 1M | 1.25 / 0.20 / 2.50 (all agents billed) | 2.50 / 0.40 / 5.00 | grok-4.20-multi-agent, … (6) |
| grok-build-0.1 | 256k | 1.00 / 0.20 / 2.00 | 2.00 / 0.40 / 4.00 | grok-code-fast-1, grok-code-fast, grok-code-fast-1-0825 |
Image gen: grok-imagine-image $0.02, -2.0 $0.04 (pricing[] by quality/resolution 0.04–0.08), -quality $0.05 (retiring 2026-11-02 → redirected to -2.0 low). Video $0.05/s, -1.5 $0.08/s. Batch API −20 % on grok-4.3 and grok-4.20 variants only. service_tier: "priority" = 2× tokens. Knowledge cutoff grok-4.6: 2026-02-01.
13. Prompt caching & provider-side state
- Automatic on all grok language models, prefix-based on the messages array; no minimum documented; no fixed TTL ("can be evicted at any time"). Reported as
usage.prompt_tokens_details.cached_tokens(chat) /usage.input_tokens_details.cached_tokens(Responses). (probed: 128–512 cached tokens on the very first request — the hidden system prompt is cached.) Cached price = 10–25 % of input price (see table). Maximise hits withx-grok-conv-idheader (chat) orprompt_cache_key(Responses) = stable session id; never edit earlier messages. - Server-side state: Responses API stores everything for 30 days by default (
store: trueechoed (probed)),previous_response_idcontinuation works (probed: recalled "pamplemousse"),instructionsincompatible withprevious_response_id(docs). For a BYOK privacy-conscious app sendstore: falseand manage history client-side (docs image page even recommends not storing).zdr_statuson/v1/metells whether the team has zero-data-retention.
14. Errors, rate limits, retries
Error body: {"code": "<string>", "error": "<message>"} (not OpenAI's {error:{message,type,code}} — the OpenAI SDK will surface e.error = that object, e.message derived).
| Case (probed) | HTTP | body |
|---|---|---|
| Invalid key | 400 | {"code":"invalid-argument","error":"Incorrect API key provided. You can obtain an API key from https://console.x.ai."} |
| No Authorization header | 401 | {"code":"unauthenticated:no-credentials","error":"No credentials presented."} |
| Unknown model | 400 | {"code":"invalid-argument","error":"Model not found: grok-99"} |
| Unsupported param | 400 | {"code":"invalid-argument","error":"Model X does not support parameter presencePenalty."} |
| Malformed body | 422 (text/plain) | serde message |
| Bad image | 400 | {"code":"invalid_image","error":"…"} |
Bad include |
400 | {"code":"400","error":"Argument not supported: …"} |
| Live search | 410 | {"error":"Live search is deprecated. …"} (no code) |
| Multi-agent on chat completions | 400 (text/plain) | Multi Agent requests are not allowed on chat completions |
| Rate limit | 429 (docs) | exponential backoff recommended |
Docs also list 403 (permissions/ACL), 404, 405, 415, 202 (deferred). Rate limits (docs, tiers by cumulative spend since 2026-01-01, never downgrade): T0 $0 → T4 $5 000; grok-4.6/4.5: 150 RPS / 50M TPM (T0) → 500 RPS / 100M TPM (T4); grok-4.3, 4.20-*, build: 37 RPS / 10M TPM → 208 RPS / 85M; multi-agent 9 RPS / 2.5M → 56 / 21M. TPM counts prompt + completion + reasoning + cached tokens. Headers in §1. Status page https://status.x.ai.
Retry/timeout recommendation for the adapter: retry 429/5xx with jittered exponential backoff (respect retry-after if present — none observed), never retry 400/422; do not retry mid-stream; use a long read timeout (≥ 5 min; xAI suggests 3600 s) because xhigh reasoning can run minutes before the first content delta (but reasoning_content deltas arrive quickly, which is a good liveness signal). Map xAI 400 "Incorrect API key" to the app's invalid credentials state (do not rely on 401).
15. Lifecycle / aliases
- May 15 2026 retirement:
grok-3,grok-4-0709,grok-4-fast-*,grok-4-1-fast-*,grok-code-fast-1,grok-imagine-image-pro. Slugs still resolve (probed:grok-3andgrok-4-fast-reasoninganswered withmodel: "grok-4.3"and were billed at 4.3 rates); reasoning slugs → grok-4.3low, non-reasoning → grok-4.3none, grok-code-fast-1 → grok-build-0.1. Do not list retired slugs in the UI; if a user types one, show the redirect. -latestaliases exist for 4.5, 4.3, 4.20-*; grok-4.6 has none yet.grok-build-latest→ grok-4.5 (surprising). Use canonical ids from/v1/language-modelsand display aliases as secondary.- grok-4.20-multi-agent is beta ("potential breaking changes"), Responses-only, no client tools.
- Docs pages under
/docs/*are gone; only/developers/*is maintained.
16. Exact streaming code that worked
OpenAI SDK (openai@7.10.0) — chat completions with reasoning + usage:
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.XAI_API_KEY, baseURL: "https://api.x.ai/v1", timeout: 3_600_000, maxRetries: 0 });
const stream = await client.chat.completions.create({
model: "grok-4.6",
messages: [{ role: "user", content: "Say hello in French, 5 words max." }],
stream: true,
stream_options: { include_usage: true },
max_completion_tokens: 200,
reasoning_effort: "low", // only 4.6 / 4.5 / 4.3
}, { headers: { "x-grok-conv-id": sessionId } });
for await (const chunk of stream) {
const d = (chunk.choices[0]?.delta ?? {}) as any;
if (d.reasoning_content) onThinking(d.reasoning_content);
if (d.content) onText(d.content);
if (d.tool_calls) onToolCalls(d.tool_calls); // arrives complete in one chunk
if (chunk.choices[0]?.finish_reason) onFinish(chunk.choices[0].finish_reason);
if (chunk.usage) onUsage(chunk.usage); // last chunk, choices: []
}Raw fetch SSE for /v1/responses (used in research/xai/lib.ts → rawSSE): POST JSON, read res.body with TextDecoder, split on \n\n, take data: lines, JSON.parse, stop at [DONE]; each event's type field is the event name (response.output_text.delta → delta, response.completed → response.usage, response.function_call_arguments.done → arguments, response.output_text.annotation.added → citation).
17. Probe results table
| # | Probe | Model(s) | Result |
|---|---|---|---|
| 00 | GET /models, /language-models, /image-generation-models, /video-generation-models, /api-key, /me, POST /tokenize-text |
— | all 200; 7 language, 3 image, 2 video models; prices in cents/100M tokens; no context_length; tokenizer returns token ids + bytes |
| 01a | tiny chat completion max_completion_tokens: 200 |
6 models | all 200, finish_reason: stop; reasoning_content present on 5/6; reasoning 46–333 tokens for "2+2"; ~2.6 s for grok-4.6 |
| 01b | streaming + include_usage |
6 models | SSE data: only; delta.reasoning_content then delta.content; final choices: [] usage chunk with cached_tokens, reasoning_tokens, cost_in_usd_ticks; [DONE] |
| 02 | param matrix (21 variants) | 6 models | see §9; penalties rejected everywhere; stop only on non-reasoning; reasoning_effort per §8; unknown params ignored |
| 03 | function call round trip, streaming | 6 models | tool call in a single chunk with complete arguments; finish_reason: tool_calls; round 2 with role: tool OK on all |
| 04 | response_format: json_schema strict |
6 models | valid JSON matching schema on all |
| 05 | vision, base64 PNG data URL | 6 models | 2×2 → 400 (min 8 px sides); 8×8 → 400 (min 512 px total); 32×32 → 200 on all, image_tokens 1–3 |
| 06 | invalid key / no auth / unknown model / bad body / retired slugs | — | 400 invalid-argument / 401 / 400 / 422 text / 200 served by grok-4.3 |
| 07 | /v1/responses: basic + reasoning.summary + encrypted_content, streaming events, function call (flat tool) + function_call_output, text.format json_schema, input_image, multi-agent on chat |
grok-4.3 (+multi-agent) | all 200; 13 event types; encrypted reasoning returned; multi-agent on chat completions → 400 |
| 08 | web_search tool streaming; legacy search_parameters; web_search on chat; include variants |
grok-4.3 | Responses OK (3 searches, url_citation annotations + inline [[N]](url), 20.9k input tokens); chat: 410 / 422; include accepts web_search_call.action.sources, no_inline_citations only |
| 09 | finish_reason on truncation; Responses reasoning.effort on 4.20/build/4.3; x-grok-conv-id + prompt_cache_key + service_tier; store default + previous_response_id + DELETE |
mixed | length; 4.20/build reject effort, 4.3 none OK; header/params accepted, service_tier: default; store default true, follow-up recalled word, delete → deleted: true |
| 10 | tools:[{type:"live_search", sources:[{type:"web"}]}] on chat |
grok-4.3 | 410 Live search deprecated |
Documentation pages used (all fetched 2026-09-08)
- https://docs.x.ai/docs/overview (redirect hub) · https://docs.x.ai/developers/quickstart · https://docs.x.ai/developers/community · https://docs.x.ai/developers/release-notes
- https://docs.x.ai/developers/models · https://docs.x.ai/developers/grok-4-6 · https://docs.x.ai/developers/pricing · https://docs.x.ai/developers/rate-limits · https://docs.x.ai/developers/migration/may-15-retirement
- https://docs.x.ai/developers/rest-api-reference/inference/chat-completions · https://docs.x.ai/developers/rest-api-reference/inference/responses · https://docs.x.ai/developers/rest-api-reference/inference/models · https://docs.x.ai/developers/rest-api-reference/inference/legacy · https://docs.x.ai/developers/rest-api-reference/inference/other
- https://docs.x.ai/developers/model-capabilities/text/generate-text · …/text/streaming · …/text/reasoning · …/text/structured-outputs · …/text/multi-agent · …/text/comparison (Responses vs Chat Completions migration) · https://docs.x.ai/developers/model-capabilities/legacy/chat-completions · https://docs.x.ai/developers/model-capabilities/images/understanding
- https://docs.x.ai/developers/tools/overview · …/tools/function-calling · …/tools/web-search · …/tools/x-search · …/tools/code-execution · …/tools/remote-mcp · …/tools/citations · …/tools/streaming-and-sync · …/tools/tool-usage-details
- https://docs.x.ai/developers/advanced-api-usage/prompt-caching (+ /how-it-works, /usage-and-pricing, /best-practices) · …/batch-api · …/priority-processing · …/deferred-chat-completions · …/context-compaction · https://docs.x.ai/developers/files · https://docs.x.ai/developers/debugging
- https://docs.x.ai/sitemap.xml (to discover the new paths) · https://ai-sdk.dev/providers/ai-sdk-providers/xai (Vercel provider)
- 404 at audit time:
/docs/guides/responses,/docs/api-reference,/docs/guides/migration,/docs/key-information/rate-limits,/docs/guides/prompt-caching,/developers/rest-api-reference/inference/legacy-deprecated,/developers/rest-api-reference/inference/account,/developers/advanced-api-usage/rate-limits,/developers/advanced-api-usage/migration,/developers/advanced-api-usage/error-handling,/developers/model-capabilities/text/chat-completions.