Disclosure: Switchbreak is an experimental publication run by Anymorph Inc., the company that builds querying.ai. This guide uses the querying.ai API, so we are describing our own product. Read more about Switchbreak.
An AI answer usually comes with two kinds of links. Some are citations: pages the answer attaches to specific sentences, which the reader sees as chips or footnotes. Others are only listed: pages the engine found while searching and returned with the answer without crediting them in the text. If you want to know which sites AI search relies on, collect both and keep them apart. A listed page was found. A cited page backs something the reader is told.
This guide shows where each kind of link sits in the querying.ai response for ChatGPT, Google AI Overviews, Google AI Mode, Perplexity and Gemini, how to separate them in code, and what to store so your counts still compare from one month to the next.
Listed and cited are two different measurements
ChatGPT shows why the difference matters. When it searches the web, its response puts the pages from its search results and the pages the answer cites into one sources list. Count every entry as a citation, and a page the engine only found looks as influential as one the answer relies on. Count only the inline citations, and you lose the pages the engine considered and passed over, which are the ones to study if your own page is among them.
Track them as two rates for each domain:
- Listed rate: the share of answers whose source list includes the domain.
- Cited rate: the share of answers that cite the domain inline.
A domain that is often listed but rarely cited is being found but not credited. That calls for a different fix than a domain that never shows up at all.
Where the links sit in each engine's response
Every engine's result arrives in the same task envelope; what changes is the response object inside it. Google AI Overviews and AI Mode read the question from query, while ChatGPT, Perplexity and Gemini read prompt, and each one falls back to the other field.
| Engine (taskType) | Listed sources | Cited inline |
|---|---|---|
ChatGPT (CHATGPT) | sources | cited and citedText on each source |
Google AI Overviews (GOOGLE) | aioverview.sources | aioverview.citationPills |
Google AI Mode (AIMODE) | sources | citationPills |
Perplexity (PERPLEXITY) | sources | No separate field |
Gemini (GEMINI) | sources | No separate field |
A trimmed ChatGPT source list looks like this. The first page is cited inline; the second came back from a search and was not cited:
"sources": [
{
"position": 1,
"url": "https://example.com/guide",
"searchQueries": ["..."],
"cited": true,
"citedText": ["..."]
},
{
"position": 2,
"url": "https://example.org/list",
"searchQueries": ["..."],
"cited": false
}
]
What each engine gives you:
- ChatGPT.
citedis true when the answer cites the page inline, andcitedTextholds the answer text each citation backs.searchQuerieslists the searches whose results included the page, so even a page that was listed and not cited tells you what ChatGPT was looking for when it found it.citationPillsholds the inline chips themselves, if you need to show them the way the reader saw them. - Google AI Overviews. The result is a search results page with the overview inside it.
aioverview.citationPillshas one entry for each source behind a citation chip, and itspositionpoints at the matching entry inaioverview.sources. Whenaioverviewisnull, Google showed no AI Overview for that query. That is not an error, and it should not count as an answer without citations. - Google AI Mode.
citationPillsuses the same fields as the AI Overview chips and appears when the answer shows citation chips. - Perplexity and Gemini. There is no separate cited field, so count
sourcesas one list and compare it across runs.
The engine reference lists every field for these and the other engines.
Send one question to several engines
The batch endpoint takes a JSON array of up to 500 tasks, and one request can mix engines. Here, one buyer question goes to three engines, asked from the United States:
curl -X POST "https://api.querying.ai/v1/async/task/batch" \
-H "Authorization: Bearer $QUERYING_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{ "taskType": "CHATGPT", "payload": { "prompt": "best project management tool for a small team", "country": "US" } },
{ "taskType": "PERPLEXITY", "payload": { "prompt": "best project management tool for a small team", "country": "US" } },
{ "taskType": "GOOGLE", "payload": { "query": "best project management tool for a small team", "country": "US" } }
]'
A well-formed batch returns 200 even when some items are rejected, so check results[].success for each task. Accepted tasks start as QUEUED. Add a webhook.url to each task to receive results as they finish, or poll GET /v1/async/task/:id until the status is COMPLETED or FAILED. The response field is absent until then.
Separate cited pages from listed ones in code
This function takes the engine you asked and the task's response, and returns the two lists:
function splitSources(taskType, response) {
const urls = (items) => (items ?? []).map((item) => item.url);
const unique = (list) => [...new Set(list)];
switch (taskType) {
case "CHATGPT":
return {
listed: urls(response.sources),
cited: urls((response.sources ?? []).filter((source) => source.cited)),
};
case "GOOGLE":
// aioverview is null when Google showed no AI Overview
if (!response.aioverview) return { listed: [], cited: [], overview: false };
return {
listed: urls(response.aioverview.sources),
cited: unique(urls(response.aioverview.citationPills)),
overview: true,
};
case "AIMODE":
return { listed: urls(response.sources), cited: unique(urls(response.citationPills)) };
default:
// PERPLEXITY and GEMINI have no separate cited field
return { listed: urls(response.sources), cited: null };
}
}
Two details keep the numbers honest. Return null rather than an empty list when an engine has no cited field, so a missing measurement never reads as zero. And normalize URLs the same way every time before you count domains: lowercase the host, drop a leading www. and remove tracking parameters, or the same page can count twice.
Store results while you still can
- Save on arrival. Public polling of a finished task works for 24 hours after completion. After that, the task endpoint returns 404
RESOURCE_NOT_FOUND. - Use webhooks first and polling as the backup. Return any 2xx to acknowledge a delivery. A failed webhook delivery is retried with growing gaps, up to five attempts in all, and then dropped, so poll inside the 24-hour window if your endpoint was down.
- Expect requeues. A task that hits a transient error moves from
PROCESSINGback toQUEUEDand is retried. Only a task that runs out of retries ends asFAILED. - Keep the context. Store the exact question, engine, country and collection time with every answer, plus the
modelfield for ChatGPT. Answers and their sources can change between runs of the same question, and a change in the model or in how you collect can move your numbers overnight. With the context stored, you can tell a real shift from a change in collection.
Go further: brand mentions and matching passages
Two options help once the basic lists work.
Set include.analysis to true and send your brand, its domains and the competitors you track. It works on all five engines above. The response then lists every brand the answer names, in order, and whether each brand you sent is mentioned and cited: analysis.targets[].cited is true when a citation is on one of that brand's domains, so it stays false if you send no domains. analysis.sources[].kind labels what kind of site each citation is, such as owned, competitor, editorial, reviews or social. It adds a charge per task, listed in the answer analysis docs.
To see which paragraphs of a cited page line up with which parts of an answer, send the task id to Source Influence while the task can still be read, or set include.sourceInfluence to true when you collect. It returns the matching paragraphs, the answer ranges they correspond to and an explanation for each link. This is post-hoc correspondence analysis: it matches text after the answer exists and does not prove that a page caused a sentence.
A setup that holds up
If you are starting from zero, keep the first version small and fixed:
- Pick five to ten questions your buyers ask, in their words, and do not edit them once tracking starts.
- Run each one daily on two or three engines in one country.
- Save every full response on arrival, with the context above.
- Report the listed rate and cited rate for each domain weekly, across many runs, rather than reading single answers.
- Write down the date whenever you change questions, engines or collection, and compare only like with like.
The quickstart walks through one task from submission to result, and plans and credit prices are on the pricing page.
Frequently asked
Is every URL in an AI answer's source list a citation?
No. ChatGPT's source list mixes pages from its web searches with pages the answer cites inline, and the cited field on each source tells them apart. Count listed pages and cited pages as separate rates.
Why do the sources change between runs of the same question?
Each run produces a new answer, and the engine can search differently each time, so the same question can return a different set of pages. Report rates across many runs instead of reading a single answer.
How long can I fetch a finished task?
Public polling works for 24 hours after a task completes. After that the task endpoint returns 404 RESOURCE_NOT_FOUND, so use a webhook or save each result as it arrives.
Does a citation prove that a page shaped the answer?
No. A citation shows that the engine attached the page to part of the answer. Source Influence matches cited paragraphs to parts of an answer after the fact, which is evidence to inspect rather than proof that a page caused a sentence.