AnswerLine Start free

, GEO · ChatGPT · Fundamentals

Query fan-out: the searches ChatGPT runs before it answers

When ChatGPT answers with web search, it does not have to search your prompt word for word. It can run several searches, narrower, rephrased or with added context, and its cited sources come from what those searches return. That set of searches is the query fan-out. ChatGPT does not show it to users, and on the ChatGPT answers this API serves today the searchQueries field comes back empty. What you can see is the result of the fan-out: the pages cited, in order, and the answer text. This post covers how to reason about the fan-out from those.

Google uses the same term for AI Mode: its announcement describes AI Mode breaking a question into subtopics and running many searches at once with a “query fan-out technique” (Google, May 20, 2025, checked 2026-09-17).

Definitions

One prompt, several searches, one cited answer A single request carrying one prompt fans out into several web searches, which ChatGPT runs but does not return today. The pages those searches return feed one answer, which comes back as result.text together with sources and citationPills, each entry carrying a url and a label. POST /v1/monitor/chatgpt one prompt one request search 1 search 2 search 3 search 4 result.text sources[] url, label citationPills[] url, domain searches ChatGPT ran: not returned today

The searches in the middle column are what the answer was built from, so they, not the prompt’s own wording, are the queries a page has to win. For ChatGPT you can’t see them today; the right-hand column is what you can see.

Why the fan-out matters

A prompt such as “iPhone 17 or Galaxy S26?” is one question to the user and several retrieval problems to the assistant: specs, camera comparisons, battery tests, prices, maybe reviews from a specific year. The pages that win those narrower searches are the ones the answer can cite. Three consequences for anyone working on AI visibility:

  1. Your target keyword is not the retrieval query. A page optimized for the prompt’s wording can miss the searches the engine ran.
  2. Fan-out queries can repeat across prompts. When different prompts in one category trigger overlapping searches, one page that serves that search is relevant to many answers.
  3. The fan-out leaves traces. ChatGPT keeps its searches hidden. The cited sources are what those searches found. Which domains get cited, and in what order, is the measurable side of the fan-out.

What the API returns

{
  "prompt": "iPhone 17 or Galaxy S26?",
  "country": "US"
}

Send it to POST /v1/monitor/chatgpt or as the payload of a CHATGPT task. The result carries text, sources[] in the order ChatGPT shows them, and citationPills[]. The contract also has result.searchQueries for the fan-out, requested with include.searchQueries: true at no extra cost, but it comes back empty on the ChatGPT answers served today.

Two details decide whether you get useful data:

curl -X POST https://api.answerline.dev/v1/monitor/chatgpt \
  -H "Authorization: Bearer $ANSWERLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "best CRM for a 10-person B2B sales team",
    "country": "US"
  }'

Aggregate cited sources across a prompt set

One answer’s sources say little; across 50 or 200 prompts they give a distribution. Count each cited domain once per answer that cited it:

from collections import Counter
from urllib.parse import urlparse

def domain(url: str) -> str:
    return (urlparse(url).hostname or "").removeprefix("www.")

def source_report(results: list[dict]) -> list[tuple[str, int]]:
    """Cited domains with the number of answers that cited them, most frequent first."""
    counts = Counter()
    for result in results:
        counts.update({domain(s["url"]) for s in result.get("sources", []) if s.get("url")})
    return counts.most_common()

results are result objects: from synchronous responses, or response.result of completed tasks.

Counting per answer, not per occurrence, keeps one answer that cites a domain several times from inflating it. Also report coverage: the share of answers that cited any sources at all. If only 40% did, your counts describe that 40%, and you should say so next to the numbers.

Reason about the searches you can’t see

Without the searches, work backwards from what was cited and what was said:

  1. Read the cited pages. Each source’s label and description say what the page is about, and so what the search that found it must have matched. A page about camera tests for two phones was found by a camera search, not by the prompt as typed.
  2. Read the answer text for subtopics. An answer that covers specs, price, battery and reviews in turn most likely searched for each. Each subtopic is a candidate query for your content plan.
  3. Group prompts by shared sources. Prompts whose answers cite the same domains probably rely on overlapping searches, and one page can serve all of them.
from collections import defaultdict

def prompts_by_domain(rows: list[tuple[str, dict]]) -> dict[str, set[str]]:
    """rows are (prompt_id, result) pairs. Maps each cited domain to the prompts whose answers cite it."""
    m: dict[str, set[str]] = defaultdict(set)
    for prompt_id, result in rows:
        for s in result.get("sources", []):
            if s.get("url"):
                m[domain(s["url"])].add(prompt_id)
    return m

This is inference, not returned data: a cited page tells you a search found it, not which search. Label inferred searches as such in reports. How AI engines choose citations covers the source side in more depth.

Turn it into work

  1. Rank topics by how many answers cite pages on them across the prompt set.
  2. Map each topic to a page. Note the page on your site that should answer it, or that none does. A missing page for a frequently cited topic is the clearest content gap you will find.
  3. Check the organic side. Inferred searches are still searches, so run the top ones through a Google Search request and see who ranks. A domain that ranks organically and is cited in answers holds both positions; one that ranks but is never cited suggests the page doesn’t answer the narrower question well. Structured SERP data shows the request.
  4. Split by market. Run the set with each country you sell in; fan-out can differ by market, and so do the cited domains.
  5. Keep the set fixed. Re-run the same prompts on a schedule so changes in the cited sources are visible, and version the prompt set when you change it. Prompt set design covers versioning.
  6. Feed new prompts back. Subtopics that recur across answers are good candidates for new monitoring prompts, because they reflect how the engine decomposes your category.

A report that people act on

A report content teams can act on has four columns per topic:

Topic (inferred from cited pages) Answers citing it Top cited domains Our page
best crm small business 34 of 150 review site A, vendor B, publisher C /crm-for-small-teams (not cited)
crm pricing comparison 21 of 150 vendor B, publisher D none
crm with email automation 12 of 150 vendor E, our domain /features/email (cited 5 times)

How to read it:

Add a fifth column with a change indicator against the previous run of the same prompt set version, and the report doubles as a trend view. Keep the denominator (“of 150”) visible, and state that topics are inferred from cited pages, not from returned searches.

Fan-out on other engines

Some engines return their searches, with different field names:

Engine Field How to get it Base price (Starter)
ChatGPT result.searchQueries include.searchQueries: true (free); empty on the answers served today $0.0006
Copilot result.searchQueries Coming; not returned yet $0.00274
Grok result.searchQueries Returned in the response: its web and X searches $0.00218

Perplexity does not return its searches. It returns related_queries, its suggested follow-up searches. Those are suggestions for the user, not searches it ran, so keep them in a separate table. The contract also defines product.generatedProductQuery on ChatGPT inline products (inlineProducts, with include.shopping), a product-level cousin of the fan-out; see ChatGPT shopping cards for the product data.

Where an engine returns searches, count each normalized search once per answer that ran it, and group near-duplicates (“best crm small business 2026”, “best CRM for small businesses”) before counting. Normalize every engine’s queries the same way so you can see which query groups every engine runs and which are engine-specific.

Run it over a prompt set

For a prompt set, send async tasks in batches (up to 500 per request) with a webhook:

[
  { "taskType": "CHATGPT", "idempotencyKey": "fanout-v3-2026-w38-crm-012-US",
    "payload": { "prompt": "best CRM for a 10-person B2B sales team", "country": "US" },
    "webhook": { "url": "https://your-app.example/hooks/answerline" } }
]

Put the prompt set version in the idempotency key, as above, so a re-run under a new version never collides with an old one. Sync, async and webhooks explains the flow.

Cost at Starter prices (/pricing has every tier): 150 prompts × 1 market × weekly (about 4.3 runs a month) × $0.0006 for a ChatGPT task ≈ $0.39 a month. include.searchQueries is free, so turning it on does not change that.

Pitfalls

  1. Reading an empty searchQueries as “no search”. On ChatGPT today it means the searches were not exposed, not that none ran; check sources.
  2. Counting occurrences instead of answers. One answer citing a domain several times should count once.
  3. Treating inferred searches as data. A search worked out from cited pages is a hypothesis; label it that way.
  4. Pooling markets early. Compare markets before merging them.
  5. Judging one run. Answers and their sources vary between runs; trend over repeated runs. AI answer volatility covers sampling.
  6. Comparing across prompt set versions. A reworded prompt produces a different fan-out. Compare runs only within one version, and when you promote a new version, run old and new side by side once so the change in counts can be attributed to the wording rather than to the engine.

See the full ChatGPT field list on the ChatGPT engine page, or start with the quickstart.

Questions

What is query fan-out in ChatGPT?

Query fan-out is the set of web searches an AI assistant runs to answer one prompt. Instead of searching the prompt as typed, it issues several rewritten or narrower searches and builds the answer from what they return.

Can I get the searches ChatGPT ran for a prompt?

Not today. The API has the field, result.searchQueries, requested with include.searchQueries at no extra cost. On the ChatGPT answers served today it comes back empty.

What can I measure instead?

The cited sources, in the order ChatGPT shows them, and the answer text. The sources show which pages won retrieval; the text shows which brands, subtopics and claims made it into the answer.

How much does the searchQueries field cost?

Nothing; include.searchQueries is free. A ChatGPT request costs $0.0006 at the Starter tier, with or without include.searchQueries.

Do other engines expose their searches?

In this API, Grok returns searchQueries: its web and X searches. Copilot's searchQueries is coming. Perplexity does not return its searches.

Should I disable web search when studying retrieval?

You can't. ChatGPT decides on its own whether to search, and disableWebSearch changes nothing today. Study retrieval on answers that cite sources; provenance.webSearchTriggered is true on those.

Try it on your own prompts

100 free requests on any engine, no card. One POST returns the answer, sources and citations as JSON.

Keep reading