Grok sources
Grok's cited sources, including the X posts it draws on.
Grok cites web pages and X posts in the same answer. `sources` returns both in order, with creator and site name where available, which matters for brands whose reputation is shaped on social platforms.
Returned in result
| Field |
|---|
sources |
Request
curl -X POST "https://api.answerline.dev/v1/monitor/grok" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"Which electric car brands do people recommend?","country":"US"}'
What Grok’s sources are
A Grok source is a page or post the Grok app cites for its answer. Grok draws on the open web and on X, so one answer can cite a review site, a news article and a handful of posts side by side. This API returns them in order as sources, web pages first, then X posts, and returns the searches Grok ran on the web and on X as searchQueries. Citation chips are removed from text and markdown.
The mix of web pages and posts is why Grok needs its own analysis. The split between posts and pages tells you whether to work on content, on social presence, or both. creator attributes post citations to accounts rather than a host, and on the web side citation share by domain works as on any other engine.
Field reference
result.sources[]
| Field | Type | Meaning |
|---|---|---|
position |
number | Position index of the source in the response. |
url |
string | URL of the source. |
label |
string | A page’s title; on an X post, the author’s display name. |
description |
string | Description of a web page. Not on X posts. |
preview |
string | Short excerpt of a web page; on an X post, the post text. |
creator |
string | On an X post, the author’s handle. |
searchEngineText, siteName, metadataTitle, image, favicon |
string | In the contract; empty on the answers served today. |
An X post’s url is https://x.com/{handle}/status/{id}. Every field but url is optional: treat url as the key and the rest as enrichment.
Fields read alongside sources
| Field | Type | Meaning |
|---|---|---|
searchQueries[] |
array of strings | The searches Grok ran on the web and on X for the response. Absent when there are none. |
model |
string, nullable | Model identifier used to generate the response. |
Grok has no citationPills field and no source-type flag; derive whether a source is a post from creator or its URL.
Annotated excerpt
Values are illustrative; field names and types follow the POST /v1/monitor/grok result schema.
{
"success": true,
"result": {
"text": "Owners mostly praise the battery life, while several recent posts mention...",
"sources": [
{
"position": 1,
"url": "https://www.example-gadgets.com/reviews/example-watch-3",
"label": "Example Watch 3 review",
"description": "Two weeks with the Example Watch 3...",
"preview": "The battery lasted nine days with GPS on..."
},
{
"position": 2,
"url": "https://x.com/example_runner/status/1960000000000000000",
"label": "Example Runner", // the author's display name
"preview": "Firmware update fixed the GPS drift on my Watch 3...", // the post text
// The author's handle: the attributable unit for social citations.
"creator": "example_runner"
}
],
"searchQueries": ["Example Watch 3 battery life reviews", "Example Watch 3 GPS problems"],
"model": "grok-example" // nullable
}
}
Cost and request options
No include flag is needed for sources, searchQueries or model. A Grok request costs $0.00218, whatever you include: include.rawResponse returns the answer as normalized stream events at no extra cost, include.rawWire returns null on Grok, and include.markdown is free too. include.html adds html, a link to the answer’s public page on grok.com, also free.
The request takes prompt and country. A request that sends state is refused with a 400 and not billed.
Example: posts versus web pages, domain share and creator frequency
The script samples each prompt several times and reports the post share of all citations, the share of answers citing at least one post, citation share by web domain, and the most frequently cited creators.
import os
import time
from collections import Counter
from datetime import date
from urllib.parse import urlparse
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
PROMPTS = ["is the Example Watch 3 worth buying", "Example Watch 3 vs Sample Band 5"]
SAMPLES = 5
DAY = date.today().isoformat()
X_HOSTS = {"x.com", "twitter.com", "mobile.twitter.com", "mobile.x.com"}
def run_tasks(tasks):
resp = requests.post(f"{API}/v1/async/task/batch", headers=HEADERS, json=tasks, timeout=60)
resp.raise_for_status()
pending = {item["task"]["id"] for item in resp.json()["results"] if item["success"]}
results = []
while pending:
time.sleep(10)
for task_id in list(pending):
body = requests.get(f"{API}/v1/async/task/{task_id}", headers=HEADERS, timeout=30).json()
status = body["task"]["status"]
if status in ("COMPLETED", "FAILED"):
pending.discard(task_id)
if status == "COMPLETED":
results.append(body["response"]["result"])
return results
def host(url):
return (urlparse(url).hostname or "").lower().removeprefix("www.")
tasks = [
{
"taskType": "GROK",
"payload": {"prompt": prompt, "country": "US"},
"idempotencyKey": f"watch-sources-{p}-s{s}-{DAY}",
}
for p, prompt in enumerate(PROMPTS)
for s in range(SAMPLES)
]
answers = run_tasks(tasks)
kinds, web_domains, creators, models = Counter(), Counter(), Counter(), Counter()
answers_with_posts = 0
for result in answers:
models[result.get("model")] += 1
has_post = False
for src in result.get("sources", []):
h = host(src.get("url", ""))
if not h:
continue
if h in X_HOSTS:
kinds["post"] += 1
has_post = True
creators[src.get("creator") or (urlparse(src["url"]).path.strip("/").split("/")[0] or "?")] += 1
else:
kinds["web"] += 1
web_domains[h] += 1
answers_with_posts += has_post
total = sum(kinds.values()) or 1
n = len(answers) or 1
print(f"{len(answers)} answers, models {dict(models)}")
print(f"post share of citations {kinds['post'] / total:.0%}, answers citing a post {answers_with_posts / n:.0%}")
for domain, count in web_domains.most_common(10):
print(f"web {domain:36} {count / total:5.0%}")
for name, count in creators.most_common(10):
print(f"post @{name:35} {count}")
When creator is missing, the script falls back to the first path segment of the URL, the handle in the x.com/<handle>/status/<id> form Grok’s posts use.
Pitfalls
Posts are published continuously, so the set Grok can cite keeps changing. Compare runs taken in the same window, take several samples per window, and put the period in the idempotency key; see sampling vs census.
Keep one list of post hosts and apply it everywhere. Missing a host variant silently moves posts into web share.
description, preview, creator and the rest are absent on some sources. Don’t make them required columns.
Without citationPills there is no inline-versus-listed distinction and no chip grouping. Compare Grok with other engines on answer share per domain, which every engine supports.
A shift in citation mix can coincide with a change in model. Segment by it before calling a trend.
Related
- Grok engine guide: calls, webhooks, geo targeting and billing.
- Monitoring Grok with an API for splitting X evidence from web evidence step by step.
- Reddit in AI citations for the same community-content question on other engines.
- Compare with ChatGPT sources and Gemini sources.
- Brand protection and news monitoring.
Questions
Do I need an include flag to get Grok sources?
No. sources and searchQueries are part of every Grok result: $0.00218. No include flag adds cost.
How do I tell X posts from web pages in Grok sources?
The result has no source-type field. An X post carries creator (the handle) and a url of the form https://x.com/{handle}/status/{id}; a web page has no creator. Classify by either.
What extra metadata do Grok sources carry?
A web page carries url, label, description and preview. An X post carries url, label (the author's display name), preview (the post text) and creator (the handle). searchEngineText, siteName, metadataTitle, image and favicon are in the contract but empty on the answers served today.
Does Grok return citationPills?
No. The Grok result has no citationPills field. Count citations from sources.
Why record the model field with Grok sources?
model is the nullable identifier of the model that generated the response. Storing it with each run lets you segment citation share when the served model changes.