ChatGPT Changed How It Picks Sources While You Were Reading My Last Post
A reader found a source pipeline I'd never seen, so I went back into ChatGPT's network traffic 10 days after the first teardown. Some of it had changed underneath me, I caught 2 of my own over-reaches, and I found the layer that shows who you lose citations to.
Someone tagged me on a Linkedin post by David Konitzny, a GEO researcher.
He posted a screenshot of his own network tab showing a result_source value I’d never seen in 2 days of staring at this stuff. bing.
Bing, sitting right there in ChatGPT’s traffic reproducible across multiple prompts on his account.
My first thought was that I’d missed it. I hadn’t. It’s better than that.
The same page came through 2 different pipes
David’s screenshot showed bing on pages from bobvila.com, a lawn mower review.
So I did the most direct experiment available. I asked my ChatGPT about Bob Vila’s electric mower rankings and aimed it at the exact page in his screenshot, then pulled the traffic.
bobvila.com/reviews/best-electric-mower/ reached me through bright. Bright Data, the scraper from Part 1. Every one of the 20 results in that thread came through bright.
The same URL, in the same week, arrived at David’s browser stamped bing and at mine stamped bright.
I went wider to make sure. A census across my last 30 conversations counted bright 558 times, labrador 21, serp 16, and bing exactly 0. I then grepped the 1.2MB of feature-flag config ChatGPT caches in my browser for anything bing-related. Nothing.
The rollout isn’t even being served to my account.
So bing is real, it’s new since late June, and it’s cohort-gated. OpenAI added a 5th retrieval pipe and is switching it on for some accounts and not others, which is a strange sentence to type about the most-watched product on the internet, and also completely normal engineering.
The AI SEO/GEO takeaway
Sort out your Bing indexation
If you land in a bing cohort, your Bing index suddenly matters. Pages missing from Bing’s index are missing from those users’ ChatGPT, so Bing Webmaster Tools plus a sitemap check is cheap insurance. It’s cohort-gated and could be pulled at any time, so file it under watch, not panic.
The runner-up layer
The best new find of the re-test is a field Part 1 dismissed in a single breath. supporting_websites sat empty in every June capture, so I catalogued the name and moved on. It’s populated now, and it’s quietly the most useful thing in the whole payload.
The wire now shows you, per claim, exactly who beat you and who you’re beating. Not at the vague level of “competitor X gets cited more”, but this sentence, this claim, you were the runner-up, here’s the page that won.
When ChatGPT cites a source for a claim, that citation now carries an array of other pages that supported the same claim but didn’t win the visible slot. Runner-up citations, each with its own result_source, sitting invisibly under the winner.
They come in 2 flavours.
The first is the domain fold from Part 1 made visible. Ask about Emirates baggage rules and the cited emirates.com page carries other emirates.com pages underneath it, the siblings that got folded into the domain’s 1 slot, including a German-locale duplicate of the exact same page. I watched a Range Rover overview page win a spec claim with the brand’s own /electric-range page demoted to support beneath it, and a canada.ca visa page beat a sibling canada.ca page the same way.
The 20-thin-pages problem isn’t a theory anymore. You can watch your own pages lose to each other. If 20 thin pages cover 1 topic, 19 of them are losing to the 20th, so build 1 strong page per claim, not a pile of weak ones.
The second flavour is competitive, and that’s the one that gave me proper Spidey sense goosebumps.
In a thread about crawler verification, the cited page was ahrefs.com/robot, and sitting underneath it as support were both semrush.com/bot and mj12bot.com, all 3 fetched through bright. One claim, 3 rivals, 1 visible winner and 2 invisible runner-ups.
It crosses the tiers too. In a lawn mower thread, bhg.com won the citation through the licensed labrador pipe with protoolreviews.com supporting it through the bright scraper, a licensed source and a scraped one backing the exact same claim, one on screen and one hidden. Elsewhere a Cloudflare blog post won with The Verge’s coverage of the same story tucked beneath it, a scraped source beating a licensed one for the same sentence.
I asked “best keyword clustering tool in 2026” and read the winners and the runner-ups straight off the console.
For GEO this is a genuinely new instrument. When the visibility tools start surfacing this field, and they will, “second place per claim” becomes a metric you can work against.
The AI SEO/GEO takeaway
Read your own runner-up data
Run your money queries, pull the stored conversation, and study how the winning page states the claim you lost, in the exact words that won it. That’s a per-claim competitive audit no paid tool offers yet, and it’s the whole reason I ended up building the extension at the end of this post.
The prompt you can’t read
One more find, and I’m including it precisely because I can’t fully open it.
Every conversation now carries 2 hidden system messages, invisible in the interface, flagged in the metadata as identity_prompt and sources_and_filters_prompt.
Sit with that second name for a moment. ChatGPT injects a dedicated prompt about sources and filtering into every conversation before it answers.
The instructions for how it should treat sources exist as a discrete, named object, and I can prove the slot is there, 64 instances across my last 30 conversations. What I can’t do is read it. The content ships empty to the browser in both the live stream and the stored conversation. OpenAI strips the text server-side and sends only the envelope. I can prove the safe exists. I can’t open it because that text never leaves OpenAI’s servers.
But I can’t read the instruction and still watch it run.
Look at what the fan-out actually types. It doesn’t just reword your question, it appends the kind of source it wants. “Emirates DIFC city check-in cost fee queue Dubai 2026 Emirates official”. “AhrefsBot official”. “official crawler IP ranges”. That “official” suffix turned up 17 times across my census, bolted onto queries for facts. That’s the sealed source-filtering prompt executing in the open. For a factual claim it’s told to go looking for the official source, and you can watch it go looking.
The implication is bigger than the find itself in my opinion.
- Source selection is a policy, not a behaviour. It lives in an instruction OpenAI can rewrite any day, without retraining a model or telling anyone. That’s the mechanism behind half this post, and it’s why the plumbing moved within 10 days of Part 1.
- Nobody can honestly sell you “ChatGPT’s ranking factors”. The real instructions are a named object that never leaves OpenAI’s servers. Everyone claiming to know them is guessing, and now you can say so with a straight face.
- You can still read the policy, just not the text. Every regularity in this series, facts routed to official pages, opinion to reviews and Reddit, “official” quietly appended to searches, is the prompt being executed in front of you. Watch the behaviour, treat each pattern as a sentence in a memo you’re reconstructing from the outside, and when the behaviour changes, assume the memo got a new draft and update your playbook with it.
The AI SEO/GEO takeaway
Be the official page for your own facts
You just watched the prompt hunt for “official” sources on factual queries, so be the official source. Facts route to official pages, opinion routes to reviews and Reddit, so keep your pricing, specs and docs in plain HTML on pages that are unmistakably yours, and let third-party coverage fight the opinion battle for you. Own the facts. If you won’t publish enterprise prices, at least say “Starts at $5000” instead of “Contact sales”, because a random Reddit comment about your product costing $20,000 gets stated as fact by ChatGPT when your own page won’t say otherwise.
3 accounts, 3 different ChatGPTs
Both of those finds came off my account, so here’s the sobering counterweight, and it decides how much you can trust any of this.
Another reader, Simone De Palma, had already challenged Part 1 from the opposite direction. He’s on free ChatGPT in Italy, and on his account every single publisher citation carries labrador, including small Italian sites that are nowhere near the “licensed national newspaper” club I described. He’d never seen bright or oxylabs at all and politely wondered where my tiers were coming from.
Put the 3 of us side by side.
| Account | What result_source shows |
|---|---|
| Simone, free tier, Italy | labrador on everything, no scraping vendors ever |
| Me, Plus, UAE | bright dominant (558 of 595), thin labrador, no bing |
| David, Germany | bing appearing, reproducible across prompts |
Same product, same field, 3 completely different pictures. And it drifts over time on a single account too. In my June capture oxylabs was live and serp had gone quiet. 10 days later oxylabs has vanished from my traffic entirely and serp is back. Nobody announced any of this. The plumbing just moved.
Which means I owe Part 1 a correction. I described labrador as a licensed tier you can’t join unless you own a national newspaper. The licensing deals are real and documented, but the tier reading was me standing in one spot and mistaking my view for the map. On free accounts labrador appears to be the default pipe for everyone, tiny Italian sites included. On my paid account it narrows to major media while Bright Data does the heavy lifting. The field doesn’t grade quality. It names which fetcher OpenAI routed your page through, for that user, in that cohort, that week.
The AI SEO/GEO takeaway
Don’t extrapolate from one account
Free tier, paid tier and different countries see different retrieval mixes for the same page. Audit your visibility from a single account and you’re auditing a cohort, not the product. Vary the tier and the geo before you declare a win or a crisis, and be just as sceptical of anyone selling you one screenshot as the truth.
I re-tested every claim, and most of the specifics had already moved
Going viral with a teardown of a system that ships weekly is a good way to develop paranoia.
So instead of writing a victory lap, I spent an evening re-running the whole thing. A 30-conversation census, 4 fresh test queries, and 2 live stream captures, checking each claim from the first post against what the wire says now.
How to read this. Everything sorts into 3 buckets and I label them as I go. The platform moved, things that genuinely changed in the traffic between 23 June and 4 July. I over-reached, things Part 1 stated more confidently than the data deserved, which I’m correcting. It held, claims I re-tested that survived. Structural facts still come from single clean captures. Anything with a count is still 1 account and directional.
| Part 1 claim | 10 days later |
|---|---|
| 4 retrieval pipes (serp, labrador, bright, oxylabs) | Platform moved. Now at least 5. bing is rolling out per cohort, oxylabs has left my mix, serp came back. |
| labrador is a licensed club you can’t join | I over-reached. Real licensing, wrong tier theory. It’s the free-tier default and a per-cohort routing decision. |
The text bucket skips the web entirely | Held. Confirmed live, with a footnote below about where that field actually lives. |
The fan-out runs site: probes and reformulates queries | Held, and improved. The stored fan-out now shows it appending source-type hints, “Emirates official”, “AhrefsBot official”. It doesn’t just rewrite your question, it specifies what kind of site should answer it. |
| Citations bind per claim, dedupe by domain | Held, and upgraded. The dedupe mechanism became visible in the re-test. |
| News citations can’t be resolved to sources | Platform moved. The news pool now persists in the stored conversation. OpenAI shipped a fix between my captures. |
| No ranking score anywhere in the traffic | I over-reached, slightly. True for web results. The local pipeline has a ranking_score slot, but it came back null in both my tests, so no exposed score, just a narrower claim. |
| Local surfaces only 2 places | Weakest claim standing. The config that said 2 went dark (more below) and the map payload carries 12 to 28 places even when few render. Treat Part 1’s line with care. |
| You’re 1 of 573 experiments | Platform moved. The feature-flag config is now hash-obfuscated. The gates are still there, the names and count are no longer readable. |
2 smaller confessions for completeness. Part 1 said a per-result weight field existed, then my re-test declared it gone, and both were sloppy. It’s a float on messages, not results, and my first search pattern only matched whole numbers. And the labrador snippets I called “near full article” are now even longer, averaging 1,217 characters against Bright Data’s 153, so that asymmetry strengthened.
The product layer also woke up. In June, product cards only rendered on queries ChatGPT classified as shopping. In the re-test, an ordinary “best lawn mowers of 2026” question came back with full merchant cards, priced in dirhams through a UAE reseller, because the commerce feed now geo-localises and fires on plain questions. The Levi’s Jeans 500 placeholder from Part 1’s bonus section is earning its keep.
One technical footnote that will save replicators some pain. A few of Part 1’s fields, turn_use_case and the per-URL moderation checks among them, never appear in the stored conversation you pull from the API. They exist only in the live response stream while the answer renders. My re-test initially declared them dead, and they’re not, they just live in a layer you have to catch in the moment. If you replicate this and a field seems missing, check which layer you’re looking at before writing your own correction post.
The AI SEO/GEO takeaway
If you sell, mind the commerce feed
Product cards now fire on plain “best X” questions and localise pricing to the buyer’s market, reseller listings and all. Feed hygiene and per-market merchant presence just stopped being a shopping-only problem and started mattering on informational queries.
Build for what holds, not for what I measured
The uncomfortable part isn’t that Part 1 needed corrections. It’s the speed. Between my capture on 23 June and David’s screenshot on 3 July, OpenAI added a retrieval vendor, benched another, started persisting the news pool, opened the commerce feed to plain queries, populated a dormant field, and obfuscated the config that used to leak its experiment count. 10 days.
That pace sorts every GEO claim into 2 piles, and the sorting matters more than any single finding.
The durable pile is mechanisms. ChatGPT reformulates your query and steers it toward source types, classifies intent before deciding whether to search at all, binds citations to individual claims, dedupes by domain, reads your official pages for facts and everyone else’s for opinion, and rewards being cleanly scrapable. Those behaviours survived every re-test, they show up in some form on the other engines too, and building for them is building on rock. Most of the levers in this post fall straight out of them: consolidate to 1 page, be the official source, read your runner-up data. One more is worth its own heading, because it’s the one people quietly get wrong.
The AI SEO/GEO takeaway
Stay reachable by the scrapers, not just the branded bots
Which vendor fetches your pages is perishable. The mechanism underneath isn’t. Commercial scraping networks are one of ChatGPT’s retrieval pipes, and on my paid account they carried nearly everything. A firewall rule that blanket-blocks scrapers can quietly cut you out of an entire cohort’s pipe, so don’t geo-block your own pages and check your WAF rules, the Cloudflare policies especially. Here’s my note on firewall blocks, and 2 free tools I built to check it the way a crawler actually sees you.
AI Crawler access checker. When someone asks ChatGPT or Perplexity about your topic, a crawler fetches your pages to build the answer. Three things quietly stop it: robots.txt disallowing a bot, a firewall challenge a bot can’t solve, or a server too slow before it gives up. This checks a domain the way a crawler sees it and shows you what’s getting through.
499 Timeout risk checker. When ChatGPT, Claude or Perplexity needs your page mid-answer, a live fetcher requests it right then, and it doesn’t wait around. Too slow and the fetcher abandons the request, your server logs a 499, and the answer gets built from someone else’s page. This times any URL the way those fetchers experience it, cold cache included.
Then there’s the perishable pile. Everything with a proper noun or a number in it. Which vendor fetches your pages, what percentage came through which pipe, which tier a field implies, what a config value is set to. That pile has a shelf life measured in days, and the industry habit of laminating a screenshot into a permanent “how ChatGPT works” slide is how agencies end up presenting a June diagram of plumbing that got re-piped in July.
So date every claim you rely on, including these. If a slide, a tool or a consultant tells you how ChatGPT picks sources and there’s no capture date on it, treat it as folklore.
Build on the mechanisms. Treat every vendor name and every percentage as weather, including mine.
Check it yourself in 2 minutes
The Part 1 recipe still works, DevTools, Network tab, search the responses for result_source. To see which pipes your account is on, open any conversation that searched the web, open the Console, and paste the snippet from Part 1. If bing shows up in your table, you’re in David’s cohort and I’d genuinely like to hear from you, because mapping who has which pipe is the next piece of this puzzle.
Just remember the layer rule from above.
The pipe labels persist in the stored conversation.
The query classification and moderation fields only exist in the live stream, so catch them while the answer is still rendering or they’re gone.
Doing that by hand for every answer is exactly as tedious as it sounds, so I built the whole recipe into a Chrome extension.
I call it FanoutFox.
FanoutFox reads your own ChatGPT session and lays out the fan-out queries, the retrieval pipe on each source, what got cited against what’s parked in supporting_websites, and which brands the answer named.
It hooks the live stream too, so it catches the fields that vanish once the answer finishes rendering. Everything in this post, in 1 click, without the Console. It’s free, it stays in your browser, and it just went live on the Chrome Web Store. Add it to Chrome, or run the recipe above by hand if you’d rather read the raw wire yourself.
What’s next
While validating all this I finally captured the one surface the first post never touched.
Deep Research.
It runs under its own internal codename, fired 38 searches off a single question of mine, and the pattern of what it cites is different enough from regular ChatGPT that it changes what kind of page you should build for it.
It also travels over a channel my original capture setup couldn’t even see, which is a story in itself.
That teardown is the next post.
Baseline captured 23 and 24 June 2026, re-tested with fresh captures on 4 July 2026, on my own logged-in ChatGPT account. Same rules as Part 1. Structural findings are read straight from the traffic and solid at a single capture. Counts are 1 account and directional. Credit where it’s due, David Konitzny found the bing pipe, Simone De Palma’s free-tier data broke my tier theory.
Norwegian entrepreneur with 20+ years in SEO. Co-founder of Keyword Insights and Snippet Digital. Based in Dubai.