How AI engines choose which sources to cite
No major AI engine publishes how it chooses which pages to cite. Vendors do document that search-grounded answers are built by running web searches, sometimes several rewritten ones, and linking the pages used. That supports a working model: a page is cited only if one of those searches retrieves it and the model then selects it while writing. Retrieval is partly observable where an engine returns its searches. Selection is not, so any claim about what the model “prefers” is a hypothesis to measure on your own prompts before acting on it.
Definitions
- Grounded answer: an answer the engine produces after retrieving web content, returned with links to that content.
- Candidate pool: the set of pages the engine retrieved for a prompt and could have cited.
- Citation: a retrieved page the answer links, returned in
sources[]and, where the engine shows inline citations, incitationPills[]. - Fan-out queries: the searches the engine ran for one prompt.
What the platforms document
AI features and your website (Google Search Central, checked 2026-09-17) says AI Overviews and AI Mode may issue multiple related searches across subtopics and data sources, which it calls query fan-out, and that AI Mode and AI Overviews may use different models and techniques, so the responses and links they show vary. To be eligible as a supporting link, a page must be indexed and eligible to be shown in Google Search with a snippet, with no additional technical requirements. Snippet controls (nosnippet, data-nosnippet, max-snippet, noindex) limit what is shown.
OpenAI
The ChatGPT search help article (checked 2026-09-17) says ChatGPT search typically rewrites the prompt into one or more targeted queries sent to search providers, may send more specific follow-up queries after reviewing initial results, and that responses using search may include inline citations and a Sources panel. The Publishers and Developers FAQ (checked 2026-09-17) says any public website can appear in ChatGPT search, and that sites should not block OAI-SearchBot for content to be included in summaries and snippets.
Perplexity
How does Perplexity work? (Perplexity Help Center, checked 2026-09-17) says it searches the internet and that each answer includes numbered citations linking to the original sources.
None of these pages describes ranking signals for citation selection.
The working model: retrieval, then selection
Stage 1: retrieval sets the candidate pool
A page that no fan-out query retrieves can’t be cited. Two consequences follow from the documentation above:
- Eligibility is a crawl and index question. For Google AI features, the bar is being indexed and snippet-eligible. For ChatGPT search, it includes not blocking
OAI-SearchBot. Check these before anything else. - The queries that matter are the engine’s. When a prompt is rewritten into several searches, a page can be a candidate without ranking for the literal prompt, and miss out despite ranking for it. ChatGPT query fan-out covers what those searches are.
Where the searches appear in responses:
| Engine | Fan-out field | Notes |
|---|---|---|
| ChatGPT | searchQueries[] |
Free with include.searchQueries: true; empty on the answers served today |
| Grok | searchQueries[] |
Its web and X searches |
| Copilot | searchQueries[] |
Coming; not returned yet |
| Perplexity | none | Not returned |
| Gemini | none | No search queries field |
| AI Mode | none | No search queries field |
Grok is the only engine that returns its searches today: ChatGPT’s list comes back empty, and Copilot’s is not returned yet.
ChatGPT decides on its own whether to search; its request’s disableWebSearch changes nothing today. For a citation study, count answers with sources (provenance.webSearchTriggered is true) and report the share without.
Stage 2: selection is where the hypotheses live
From the candidate pool, the answer links a subset. The traits commonly credited for selection are plausible, but the engines have not confirmed any of them. Treat each as a hypothesis with a test:
| Hypothesis | Why it’s plausible | How to test it |
|---|---|---|
| Pages with specific, extractable claims (a number, a definition, a named recommendation) get cited more | A generated sentence needs a source that supports it directly | Code cited vs retrieved-but-not-cited pages for “contains a direct answer sentence”; compare rates |
| Structured pages (question-shaped headings, tables, lists) get cited more | Clean passages are easier to lift | Same comparison, coding headings and tables |
| Topical fit beats domain size | The page that answers the fan-out query is the best support | Compare cited domains with top-ranking domains for the same fan-out queries |
| Recent pages win on time-sensitive prompts | Freshness matters for prices, releases, news | On ChatGPT, compare sources[].datePublished (omitted when unknown) between time-sensitive and evergreen prompt groups |
| Engines differ in how many sources they cite and how much they overlap with Google | Different retrieval providers and models | Source counts per engine; domain overlap between engines and with Google organic results |
The control group is the main design choice. Google organic results for the engine’s own fan-out queries, where it returns them, give you pages that were plausibly retrieved but not cited. For ChatGPT, whose list is empty today, Google organic results for the prompt itself are a looser control. Cited pages alone show what winners look like; comparing them with that set shows what separates them from the rest.
Measuring it per engine
The script below runs one prompt through ChatGPT, Gemini, Copilot, Perplexity and Grok, collects cited domains and any fan-out queries, then runs the prompt and each fan-out query through Google Search to build the organic comparison set. It uses synchronous calls for readability; for a full study, submit the same payloads as async batches.
import os
from itertools import combinations
from urllib.parse import urlsplit
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}", "Content-Type": "application/json"}
ENGINES = {
"chatgpt": ("/v1/monitor/chatgpt", {}),
"gemini": ("/v1/monitor/gemini", {}),
"copilot": ("/v1/monitor/copilot", {}),
"perplexity": ("/v1/monitor/perplexity", {}),
"grok": ("/v1/monitor/grok", {}),
}
def call(path: str, body: dict) -> dict:
resp = requests.post(API + path, json=body, headers=HEADERS, timeout=330)
resp.raise_for_status() # a 500/502 from a synchronous call is final: record it, don't retry
return resp.json()["result"]
def domain(url: str) -> str:
return urlsplit(url).netloc.lower().removeprefix("www.")
def study(prompt: str, country: str = "US") -> dict:
out = {"engines": {}, "queries": {prompt}, "organic": set()}
for name, (path, extra) in ENGINES.items():
r = call(path, {"prompt": prompt, "country": country, **extra})
sources = [s for s in r.get("sources", []) if s.get("url")]
out["engines"][name] = {"count": len(sources), "domains": {domain(s["url"]) for s in sources}}
out["queries"].update(r.get("searchQueries", []))
for q in out["queries"]:
g = call("/v1/monitor/google", {"query": q, "country": country})
out["organic"].update(domain(o["link"]) for o in g.get("organicResults", []) if o.get("link"))
return out
def report(s: dict) -> None:
for name, e in s["engines"].items():
in_organic = len(e["domains"] & s["organic"]) / len(e["domains"]) if e["domains"] else 0.0
print(f"{name}: {e['count']} sources, {len(e['domains'])} domains, {in_organic:.0%} also in organic")
for a, b in combinations(s["engines"], 2):
da, db = s["engines"][a]["domains"], s["engines"][b]["domains"]
jaccard = len(da & db) / len(da | db) if da | db else 0.0
print(f"overlap {a}/{b}: {jaccard:.2f}")
report(study("Which password manager is best for a small business?"))
The organic set pools the prompt and every fan-out query from every engine, a deliberately broad control; for a stricter test, keep each engine’s queries and organic results separate. One prompt proves nothing: run a fixed set and look at distributions per engine, not single values. Run your own AI citation study covers study design.
What it costs
At Starter-tier prices: ChatGPT $0.0006, Gemini $0.0006, Copilot $0.00274, Perplexity $0.0015 and Grok $0.00218, so $0.00762 per prompt for the five engines, plus $0.00103 per query through Google.
For an illustrative study of 50 prompts, counting one Google query each (the prompt itself; every fan-out query adds $0.00103): 50 × $0.00762 = $0.381 plus 50 × $0.00103 = $0.0515, so $0.4325. New accounts get 100 free requests (each engine call or Google query is one), enough to validate the pipeline on a handful of prompts first. See /docs/credits and cost planning.
Reading citations correctly
sources[]vscitationPills[]. Sources is the answer’s list of referenced pages; pills are inline citations tied to parts of the text. Each pill entry has apositionthat is a 1-based index intosources.citationPillIdis 0-based on ChatGPT and 1-based on the other engines that return pills. Grok has no pills.- Citation isn’t mention. A page can be cited without your brand being named, and the reverse. Track both (mentions vs citations).
- Answers vary between runs. The same prompt can cite different sources on repeated runs, so a single run is a sample. See answer volatility.
- Markets differ.
countrychanges answers, and so doesstateon Gemini; compare like with like. - Third-party platforms count. Cited domains are not limited to brand and publisher sites; forums and review platforms can appear too, which is why Reddit citations deserve their own analysis.
The working loop
- Fix eligibility. Confirm
OAI-SearchBotand Googlebot can crawl your key pages and that snippet controls aren’t suppressing them. - Collect the candidate pool. Run your prompt set per engine, and Google organic results for the prompts and for any fan-out queries an engine returns.
- Label cited vs not cited. For pages in the pool, mark which were cited, per engine.
- Test one hypothesis at a time. Code the trait (direct answer sentence, table, date) on both groups and compare rates.
- Change pages based on what held up. Update or publish pages where your data shows a gap. Structured data and AI visibility covers one frequently raised trait.
- Re-run on the same prompts. Compare citation rates over several runs before concluding a change worked.
Field details for each engine are on /engines/chatgpt/sources and the other engine pages.
Questions
Do AI engines publish how they pick citations?
Not in detail. Google, OpenAI and Perplexity document that their answers are built from web searches and link to sources, and Google and OpenAI describe running several related or rewritten searches, but none publishes a selection algorithm.
What does a page need to be eligible for citation in Google AI Overviews or AI Mode?
Google Search Central says a page must be indexed and eligible to be shown in Google Search with a snippet, and that there are no additional technical requirements. Snippet controls such as nosnippet and max-snippet limit what is shown.
What does a site need to appear in ChatGPT search?
OpenAI's Publishers and Developers FAQ says any public website can appear, and that for content to be included in summaries and snippets the site should not block OAI-SearchBot.
Do ChatGPT, Perplexity and Gemini cite sources differently?
The same prompt can return different numbers and sets of sources on different engines, but claims about each engine's preferences are not publicly established. Measure it for your own prompts by comparing source counts per engine, domain overlap between engines, and overlap with Google organic results for the same prompts.
Which engines expose the searches they ran?
ChatGPT has a searchQueries field (include.searchQueries, free), but it comes back empty on the ChatGPT answers served today. Grok returns searchQueries: its web and X searches. Copilot's searchQueries is coming. Perplexity, Gemini and AI Mode return no search queries.