How to capture ChatGPT
fan-out queries
When ChatGPT searches the web, it rewrites your question into a handful of narrower queries and searches for those. Here is how to see them, with code and with Moose.
OpenAI exposed 12 distinct ChatGPT fan-out queries for this prompt.
best AI security platforms for SMB
- best AI security platforms SMB 2026 endpoint email cloud managed detection small business
- Gartner market guide SMB cybersecurity platforms AI 2026
- small business cybersecurity platform AI features official pricing MDR email security official
- site:microsoft.com Defender for Business official SMB endpoint protection
- site:huntress.com managed EDR small business official
- site:sentinelone.com Singularity Core small business official
- site:sophos.com Intercept X small business official
- site:crowdstrike.com Falcon Go small business official pricing
- site:bitdefender.com GravityZone Small Business Security official pricing
- site:todyl.com managed security platform SMB official
- best MDR for small business 2026 independent comparison review
- SMB email security AI phishing protection official pricing 2026
What is a ChatGPT fan-out query?
A fan-out query is one of the several search queries ChatGPT writes for itself when it decides to search the web. You type one question. Before it answers, the model rewrites your question into a set of narrower, more specific searches, runs all of them, reads what comes back, and synthesises a single answer.
The searches it wrote are the fan-out queries. They are not what you typed and they do not appear in the answer. They are the queries your pages need to show up for.
How to capture fan-out queries with code
There is no public API for this. The only way to see the queries yourself is to drive a logged-in browser and read the search tool calls out of the response stream. Here is a working starting point, and the list of what will break.
# fanout.py · read ChatGPT's own search calls off the wire import asyncio, json from playwright.async_api import async_playwright PROMPT = "best AI security platforms for SMB" found = set() def harvest(evt): msg = evt.get("message") or {} # the fan-out queries ride inside the search tool call if msg.get("author", {}).get("name") != "web": return for part in msg.get("content", {}).get("parts", []): if isinstance(part, str) and part.startswith('search("'): found.add(part[8:-2]) async def main(): async with async_playwright() as pw: # a real logged-in profile. clean headless gets challenged. ctx = await pw.chromium.launch_persistent_context( "~/.chatgpt-profile", headless=False) page = await ctx.new_page() async def on_response(res): if "/backend-api/conversation" not in res.url: return body = await res.text() # SSE, not JSON for line in body.splitlines(): if line.startswith("data: "): try: harvest(json.loads(line[6:])) except ValueError: pass page.on("response", on_response) await page.goto("https://chatgpt.com/?search=1") await page.fill("#prompt-textarea", PROMPT) await page.keyboard.press("Enter") await page.wait_for_timeout(45_000) # no "done" signal print(json.dumps(sorted(found), indent=2)) asyncio.run(main())
// fanout.mjs · same idea, node + playwright import { chromium } from 'playwright'; const PROMPT = 'best AI security platforms for SMB'; const found = new Set(); // headless gets challenged, so this opens a real window const ctx = await chromium.launchPersistentContext( './chatgpt-profile', { headless: false }); const page = await ctx.newPage(); page.on('response', async (res) => { if (!res.url().includes('/backend-api/conversation')) return; const body = await res.text().catch(() => ''); // SSE for (const line of body.split('\n')) { if (!line.startsWith('data: ')) continue; let evt; try { evt = JSON.parse(line.slice(6)); } catch { continue; } const msg = evt?.message; if (msg?.author?.name !== 'web') continue; for (const p of msg?.content?.parts ?? []) { if (typeof p === 'string' && p.startsWith('search("')) found.add(p.slice(8, -2)); } } }); await page.goto('https://chatgpt.com/?search=1'); await page.fill('#prompt-textarea', PROMPT); await page.keyboard.press('Enter'); await page.waitForTimeout(45_000); // no "done" signal console.log([...found].sort());
A clean headless context gets challenged, so you end up running a visible Chromium with your own session in it.
The author name, the parts array, the search("...") wrapper: none of it is a contract. It has changed before and will change again.
If the model answers from memory there is no fan-out at all, and nothing tells you that is what happened. You get an empty set.
No schedule, no deduplication across runs, no comparison with last week. Capturing once is easy. Capturing repeatedly and noticing what moved is the work.
Automating a logged-in ChatGPT session is a decision about your own account. Read the terms before you do it.
With Moose, you ask in chat
Same capture, same source, without the browser work. Moose keeps every run, so you can compare this week's query set against last week's.
Type "capture ChatGPT fan-out queries". No workflow to configure.
Any question a buyer would type. Moose runs it and captures the search calls that come back.
The queries land in your library, exportable, and ready to feed a brief or a coverage check.
This one is not available on BYOK plans. The capture has to come straight from OpenAI, and routed access through OpenRouter does not expose ChatGPT's internal search queries. On a key-based plan, everything else in Moose still works, including the instant visibility check across five AI search engines. This one feature does not.
What to do with the queries
Those twelve searches decide which pages get pulled and read, so they matter more than the exact phrasing your buyer typed.
When ChatGPT wants an official answer it goes to specific domains. If yours is not among them for your own category, that is the gap.
Pricing, comparison, official spec, independent review: the fan-out tells you which of those the engine went looking for, so you know what one page has to cover.
The query set changes as the model and the index change. New queries appearing is an early read on where the category is moving.
Questions people ask
Are fan-out queries the same as the citations ChatGPT shows?
No. Citations are the pages the engine used. Fan-out queries are the searches that found them.
Do the queries change if I run the same prompt again?
Usually yes, a little. The core queries are stable and the long tail moves. Run the same prompt weekly and watch which queries appear and disappear.
Will optimising for fan-out queries get me cited?
No one can promise that. The queries show what the model went looking for. If none of your pages answer those searches, they will not be read. If they do, they can be.
Do other answer engines fan out too?
Most of them do. Perplexity, Google AI Mode and Gemini all break a question into searches before answering. What differs is how much of it they show you. Google is the other one worth capturing, and it works differently enough to have its own page.
More things to ask Moose
All use casesThe same question for the other engine. The searches Google writes for itself, and who it cites.
One question, five engines, about a minute. Free on every plan.
A semantic index of every page, built and kept on your computer. Ask it what you cover.
Top-10 results, People Also Ask, the AI Overview and its sources, all cited in the brief.
Twelve AI crawlers checked one by one, Bing index checked in a browser.
When share drops, Moose finds the page, prepares the fix and waits for your approval.
See the searches behind
the answer.
Download Hi, Moose, open a chat, and ask. Fan-out capture needs the managed plan. The app is free to install and the visibility checks are free to run.