How do you use Grok through an API?
Last updated August 24, 2026
Two routes cover Grok. GET grok/config is free and reports whether your X account is eligible, its default model, and the three modes: auto maps to grok-4-auto, fast to grok-3-latest, expert to grok-4. POST grok/chat sends a prompt of up to 20,000 characters and returns the answer plus its citations, billed at $0.0040 a call.
Every rate here is the pricing TwitterAPIs publishes. The billed rate is $0.0008 per call; $0.04 per 1,000 tweets is derived from it at a full 20-tweet page, which is the default page size rather than a guaranteed yield (source: twitterapis.com/pricing).
This is Grok inside X, not a standalone model endpoint
The search term is ambiguous, so start with what these two routes actually are. They do not front a separate model service with its own developer account and its own token billing. They drive the Grok that lives inside X, acting as the X account whose session you attached, and the conversation plus its chat items are created on that account. The usage counts against it too. What you get in exchange is a model reading X live, which is why a bare status URL works as a prompt at all, and why the answers reach posts a general model has never seen. The tradeoff is that eligibility is a property of the account, so a key on its own gets you nothing until a session is registered, and an account X considers ineligible cannot be made eligible from this side.
Check eligibility before the first prompt
Eligibility belongs to the X account rather than to your key, so GET grok/config answers for the specific account you attached. It hands back eligible, an ineligible_reasons array carrying X's own wording verbatim, free_access_enabled, default_mode, default_model_option_id, grok_version, a modes object, and a model_options list whose entries each hold id, name, description, mode, is_enabled, is_analyze_enabled and is_enhance_enabled. The route takes no parameters at all and is not metered, so call it once at startup and cache what it says rather than re-reading it before every prompt. One trap deserves a branch of its own: a 200 carrying eligible false alongside an empty ineligible_reasons array means the configuration could not be read, not that X declined to explain itself, and those two cases want different handling in your code.
Sending a prompt to grok/chat
POST grok/chat carries message in the JSON request body. A status URL alone is a legitimate prompt, because Grok resolves it in real time and answers with a structured summary of that post rather than a link. mode selects auto, fast or expert and defaults to auto. image_count bounds how many images Grok may generate, from 0 to 4, defaults to 4 which is X's own default, and clamps anything outside the range instead of erroring, so a bad value never costs you a rejected call. conversation_id is optional: omit it and a fresh conversation is opened and its id returned. Either message or messages must be present, and when both are sent message wins, so a stale history array cannot quietly override the prompt you meant to ask.
What comes back with the answer
The reply carries answer with the reasoning and tool chatter stripped out, so you get text a reader would want rather than a transcript. citations lists the pages the answer used, each with url, domain, snippet and sometimes title, accumulated across every search Grok ran for that turn and de-duplicated by url, with citation_count reporting the total after that collapse. Some entries arrive with a url and a snippet and no title at all, so render defensively. tool_calls records what it did, each entry holding id, tool and args; the observed tool is web_search and args carries the query it issued. requested_model is the id derived from your mode, and model is what actually answered, which matters because auto resolves at X's end rather than at yours.
Continuing a conversation on a stateless route
Nothing about a turn is kept on this side: no conversation, no turn, no prompt text, no answer text. Each reply hands back conversation_id, response_id and user_response_id, and continuing a thread means echoing conversation_id along with the earlier turns in messages, oldest first. Each entry takes role, which is user or assistant, plus content, or X's own sender number, 1 for you and 2 for Grok, plus message. That makes the history yours to store and yours to trim, which is unusual for a chat surface and is part of why the route can be priced per turn rather than per token. Sent with messages alone, the final entry has to be a user turn, and a history ending on a Grok turn is refused rather than re-sent as your prompt.
Whose account runs the inference
A registered session is required rather than optional here, because the chat items are created on your own X account and the Grok usage lands there. Pooled reads elsewhere on the surface answer happily without one; this family will not, and the failure arrives as a refusal rather than as an empty result. That has a practical consequence for teams: two people sharing a key but wanting separate Grok histories need separate sessions, since the conversation lives on the X side attached to whichever identity is acting at the time. It also means the account's own Premium tier, region and age decide whether Grok answers at all, which is exactly what grok/config reports back through eligible and the ineligible_reasons array beside it.
The two caps that refuse a call before it costs anything
Two limits are checked locally before anything leaves for X. A prompt over 20,000 characters is refused with a 400, and a history of more than 100 prior turns is refused the same way. Both rejections happen before the upstream call, so an over-long request never spends a call, which makes it safe to let user input run into the cap rather than pre-truncating defensively and losing detail you might have wanted. A third case sits at the other end of the request: a reply carrying no readable answer returns 422 and is not billed either. Read ok to tell that apart from a good turn, and note separately that answer can be null on a response that did return other fields, so the two checks are not the same check.
Why a turn costs $0.0040 rather than the read rate
Four tenths of a cent is five times the standard read price, and the reason is the shape of the call rather than model tokens. The reply is buffered into one complete JSON response instead of streamed, so the connection is held open for as long as Grok takes to search, read and write. A turn that runs several web searches occupies a worker for that whole span, and the flat per-turn charge covers it whether the answer is one sentence or a page, which is why long answers are not penalised. Config costs nothing, so a session that checks eligibility once and then asks forty questions works out at forty times $0.0040, sixteen cents. The $0.50 signup credit is therefore 125 chat turns before any card is involved.
Throughput when you fan prompts over a list
One key is allowed 600 requests a minute with 20 in flight at once, and for this family the concurrency figure is the one that binds. Because each turn holds its connection until the answer is complete, twenty slow turns can occupy every concurrent slot while the per-minute allowance sits mostly unused, which is the opposite of how a fast read endpoint behaves. Size a worker pool at twenty and let it drain rather than firing a hundred prompts and hoping something absorbs them. When you are running Grok across a list of posts, keep the pool fixed, batch the list, and record the model field per row, because auto can resolve differently from one turn to the next and you will want to know which model produced which answer. $0.0040 a call, per the rates we publish.
The Grok surface at a glance
| Item | Value | What it means in practice |
|---|---|---|
| mode auto | grok-4-auto | The default. X resolves the real model server side, so model can differ from requested_model. |
| mode fast | grok-3-latest | The option X labels Fast in the model_options list. |
| mode expert | grok-4 | The option X labels Expert in the model_options list. |
| Prompt cap | 20,000 characters | Over it, a 400 is returned before any upstream call, so nothing is spent. |
| History cap | 100 prior turns | Same local refusal. The last entry in messages must be a user turn. |
| image_count | 0 to 4, defaults to 4 | Values outside the range are clamped rather than rejected. |
| grok/config | Free | Takes no parameters and is not metered. Call it once at startup. |
| grok/chat | $0.0040 per turn | A reply with no readable answer returns 422 and is not billed. |
This dictionary documents every available field for each object type.
Questions and answers
- Is this the same as calling a Grok model API directly?
- No, and the difference is worth being clear about up front. There is no separate model account here and no token billing. These routes drive the Grok inside X, acting as the account whose session you registered, so the conversation is created on that account and the usage counts against it. In exchange you get a model reading X in real time, which is why a status URL works as a prompt.
- Does Grok answer as my own X account?
- Yes. The chat items are created on the account whose session you registered, and the Grok usage counts against that account rather than a shared pooled one. That is also why eligibility is read per account rather than per key: Premium tier, region and account age all sit on the X side, so grok/config answers for your specific account and tells you nothing about anybody else's.
- What does grok/config cost?
- Nothing. It is one of the free routes and is not metered, so calling it at startup to read eligible, default_model_option_id and the model_options list adds no spend at all and needs no parameters. The chat route is the priced half at $0.0040 per turn. Reading config first is plainly cheaper than discovering ineligibility from a failed chat call after the fact.
- Can I send a tweet URL as the prompt?
- Yes, a bare tweet or status URL is a valid message on its own. Grok reads X in real time, so it answers about that post and returns a structured summary rather than a link dump or a refusal. The answer text arrives in the answer field, with the pages it consulted listed separately under citations, each carrying url, domain and a snippet.
- How do I hold a multi turn conversation?
- Echo the conversation_id you were given, and pass the earlier turns in messages, oldest first. Each entry takes role and content, or X's own sender number plus message. The cap is 100 prior turns, and exceeding it is refused with a 400 before any upstream call, so an over-long history never spends money on a request that was never going to run in the first place.
- Why did the model field not match what I asked for?
- requested_model is the id derived from your mode, and model is what actually answered. Because auto resolves at X's end rather than here, asking for grok-4-auto can come back answered by grok-4. Read the model field whenever you need to record which model produced a given answer, rather than assuming the request you sent was honoured exactly as written when you queued it.
- What happens if there is no answer text?
- A reply carrying no readable answer returns 422 and is not billed, and the ok field is how you tell. Separately, answer can be null on a response that did return other fields, so branch on ok first and treat a null answer as a second, softer case. An eligible false with an empty ineligible_reasons array is a third thing again: the config could not be read at all.
- Why does one turn cost more than a normal read?
- Because the call holds a connection open rather than fetching a stored record. The reply is buffered into a single JSON response instead of streamed, so a worker stays occupied while Grok searches, reads and writes. The price is flat per turn regardless of answer length, which means long answers are not penalised, and the free config route keeps eligibility checks off the bill entirely.
- How many prompts can I run at once?
- Twenty concurrent requests per key, inside a ceiling of 600 a minute. For chat the concurrency number is the binding one, since every turn occupies its slot until the answer is complete rather than returning in milliseconds. A fixed pool of twenty workers draining a queue is the right shape here; firing a hundred prompts at once does not make any single one of them finish sooner.
- Do you store my prompts or the answers?
- No. The route is stateless by design: no conversation, no turn and no prompt or answer text is kept on this side at all. That is precisely why continuing a thread means sending the prior turns back yourself in messages. The identifiers returned, conversation_id and response_id, point at state living on the X side rather than at anything held here on your behalf.
Keep reading
Start with $0.50 in free credits
No credit card. Roughly 12,500 tweets to test every endpoint.