Raw-stream hidden signals:what ChatGPT and Perplexity actually send
TL;DR: Behind the ChatGPT and Perplexity answer you see is a raw server-sent-event stream carrying dozens of undocumented fields our AI Hotel Landscape pipeline already stores but had never parsed. We walked that stream, by key presence rather than a hardcoded path, against our own captures — not a third party's — and re-tested six claimed hidden signals. Two held up (a 17-head Perplexity intent classifier, confirmed 69/69; a ChatGPT citation-competitor field, real but weaker than advertised), and three came back as clean, structural zeros: no brand priors in ChatGPT's “thoughts,” no measurable model escalation, and no Perplexity trust tiers anywhere in 519 checked records.
Executive Summary
We had our own data. We had never actually parsed it.
Bright Data's capture pipeline stores the full raw server-sent-event (SSE) stream from ChatGPT and Perplexity on every hotel prompt in the AI Hotel Landscape corpus — but until this retest, nobody had walked that field for anything beyond the parsed answer text. It is dozens of undocumented keys deep: classifier scores, retrieval-tier tags, citation-competition graphs, model routing metadata. The AEO knowledge base had flagged six specific claims about what lives inside that stream, most sourced from a different capture surface (a live-browser Chrome extension) than the one that actually feeds our production data. This retest re-ran all six against our own Bright Data captures — a read-only walk of public.fanout_captures, no third-party dataset involved.
The result is a genuine mixed verdict, not a clean win: one signal (Perplexity's intent classifier) is exactly as documented, one (ChatGPT's citation-competitor field) is real but weaker than the KB entry implied, and three — ChatGPT narrating brand priors, ChatGPT's model escalation, and Perplexity's trust tiers — came back as clean, reproducible zeros in this pipeline and this query set today. We report the zeros as prominently as the hits; that is the point of re-testing.
Read the negative results as scoped, not universal. A zero in our data means the signal did not appear in our specific capture pipeline and query library — a narrow set of single-turn, simple hotel prompts fired through Bright Data's SSE tee. It does not prove the phenomenon can never occur on a different client, a richer multi-turn conversation, or a paid-tier account. Every refuted finding below says exactly what would change that verdict.
What held up
Two of six signals survived contact with real data — one confirmed exactly as claimed, one real but weaker than the source implied.
Perplexity's 17-head intent classifier (confirmed, 69/69)
Every parseable Perplexity snapshot carries a populated classifier_results.mhe_predictions_full block — a router scorecard of 17 named intent heads (skip_search, places_search_intent, shopping_intent, nav_intent, comet_nav_widget_combined_target, and 12 others), each carrying a probability and threshold. On a query for “boutique hotel with good coffee,” the panel fired places_search_intent at probability 1.0 (threshold 0.85) and comet_nav_widget_combined_target at 0.97 (threshold 0.5), against a shopping_intent of 0.11 (threshold 0.7) — for a query that never explicitly asked for navigation or shopping. Two heads, skip_search and personal_search, deviate slightly: they carry only a boolean, no probability or threshold, in every example checked.
response_raw field this whole retest depends on (see Section 3). “Confirmed” here means confirmed for the window where it's observable, not confirmed for current, live data.ChatGPT's winner-vs-runner-up citations (partially confirmed)
A supporting_websites structure appears in 112 of 146 (77%) recent ChatGPT hotel captures, with 141 winner/runner-up groups total — each pairing the citation ChatGPT actually used against one or more competitors it considered. Manually reading 10 of those pairs, roughly 7 in 10 are genuine same-claim competitors — e.g. Bangkok's winner (“The 10 best luxury hotels in Bangkok in 2026 | Latitude”) against a runner-up making the identical claim (“The Best Luxury Hotels in Bangkok for 2026 · La Revue des Hôtels”) — while roughly 3 in 10 are only topically adjacent, like a curated Beijing top-10 list paired against a Reddit thread asking for help choosing between four hotels.
snippet field on both winner and runner-up nodes is an empty string. Bright Data's parse never populates it for these citation nodes. That means the “surrounding claim” comparison this signal implies reduces entirely to title-string similarity; there is no snippet text to confirm sentence-level competition. This is itself worth fixing before supporting_websites becomes a metric — title similarity alone is a much weaker signal than a same-sentence claim match.Perplexity's mode/model fields (partially confirmed — real fields, no escalation observed)
search_mode, user_selected_model and display_model all exist exactly where claimed. But across all 69 parseable records, search_mode was null every time (never STUDY or RESEARCH), and user_selected_model and display_model were identical (“turbo” / “turbo”) with zero divergences — including on hotel prompts phrased conversationally enough that an escalation would be plausible if the account/config ever triggered one. The scraping setup's consistently present _extras.pro_search_mode: “auto” hints this account tier may simply never escalate; a larger or paid-tier sample would be needed to say more.
What didn't
Three claimed signals came back as clean, reproducible zeros — not “rare,” genuinely absent given how each is currently defined and how our pipeline captures data. We report them as negative results rather than quietly dropping them.
thoughts blocks are present in all 146 parseable records, but every one of the 16 distinct thought strings observed is boilerplate — “Searching 10 websites” (28×), “Searching 11 websites” (23×), down to a single “Searching www.cntraveler.com” (a bare domain, not a brand mention). thoughts.content is always an empty string; no capitalized multi-word proper-noun candidate — no hotel brand, chain, or destination-specific reasoning — appears anywhere in the stream, before or after the first search event.Scope of the refutation: this used our own Bright Data SSE-tee capture on simple, single-turn “best/cheap/luxury hotels in [city]” prompts — not the live-browser capture the original claim was sourced from. It's plausible the tee only surfaces the UI-facing summary type of thoughts block, or that more complex, comparative, multi-turn prompts would surface narrated reasoning these queries never trigger. This refutes the signal as it manifests in our capture pipeline and query set today — it is not a claim that ChatGPT never narrates priors under any circumstances.
default_model_slug reads “auto” in 100% of records — never a concrete model name to compare against. The resolved model underneath does vary (gpt-5-6 on 130 records / 89%, gpt-5-5-mini on 16 / 11%), but that's normal Auto routing, not escalation — the metric's own definition requires a concrete requested model, so the qualifying denominator is 0/146 by construction. This exactly reconfirms a caveat already written into our own pipeline code: it isn't a new finding, it's a clean re-confirmation that this framing is unmeasurable unless the capture trigger is changed to request a concrete model.trust:{level,name,description} structure exists in 69 fully-parsed snapshots or in 450 additional records substring-scanned for the literal string "trust" — 300 spread across Dec 2025–Aug 2026, plus 150 from the single most recent capture day. The domain-scope field that does exist in the still-populated window is a bare slug, web_results[].meta_data.citation_domain_name (e.g. “hotelparisbastille”), nothing close to a {level, name, description} trust object. One record inspected in depth (583 KB of inner SSE text, 140 data: lines) had zero trust keys anywhere.This one comes with the biggest confound of the retest — see Section 3. The most likely explanation isn't that trust tiers were fabricated; it's that they live on a different capture surface entirely.
The finding underneath the findings: the substrate is vanishing
The single biggest result of this retest is a data-availability one, not a signal-semantics one — and it dominates every Perplexity verdict above.
Bright Data's Perplexity dataset feeding fanout_captures stopped reliably including a usable inner response_raw SSE string well before this retest's query window: intermittent from around February 2026 (a same-day mix of populated and empty rows), consistently empty by roughly April 2026, and by August 2026 the response_raw key — along with answer_html, answer_text and source_html — is gone from the payload schema entirely, replaced by a new links_attached key. This is a scraper/schema change on Bright Data's side, unrelated to fanout_captures' own 30-day retention purge on ChatGPT raw payloads — a second, separate reason the same field can be missing depending on the engine.
Net effect: every structural (parsed) Perplexity check in this retest is drawn only from the Dec 2025–Feb 2026 window, because that's the only period where the substrate exists in this table at all — even though non-null raw_response rows exist all the way through August 2026 (they are simply missing the useful inner text). We could not structurally test a single post-Feb-2026 Perplexity record for any of these three signals, even with a much larger sample.
| Window | Perplexity response_raw | What it means for this retest |
|---|---|---|
| Dec 2025 – ~Feb 2026 | Reliably populated | The only window where trust_tiers, router_scorecard and mode_escalation could be structurally parsed at all |
| ~Feb 2026 | Intermittent | Same-day mix of populated and empty rows — transition period |
| ~Apr 2026 onward | Consistently empty | Non-null response_raw rows exist, but the inner SSE text is missing |
| Aug 2026 (current) | Key removed entirely | response_raw / answer_html / answer_text / source_html gone; replaced by links_attached |
Signal tracker
All six signals, one table. Once public_dashboard.pd_weekly_raw_stream_signals ships, this table re-runs every week from live capture data and the rows below update automatically; until then it renders the frozen counts from this retest — read-only retest, August 2026.
| Engine | Signal | Verdict | Occurrences | Sample checked |
|---|---|---|---|---|
| ChatGPT | supporting_websites (winner vs. runner-up citations) Real and common, but weaker than advertised: every inspected snippet field is empty, so the "same claim" judgment reduces to title-only comparison — about 7 in 10 manually-read pairs genuinely compete, 3 in 10 are only topically adjacent. | Partially confirmed | 141 groups | 112/146 records (77%) |
| ChatGPT | priors (brand names in thoughts before search) 100% of observed thoughts text (16 distinct strings) is boilerplate progress messages ("Searching N websites") — zero brand, chain, or destination-specific reasoning in any record. | Refuted | 0 occurrences | 146/146 records |
| ChatGPT | model_escalation (default_model_slug vs. resolved) default_model_slug is "auto" on every single record — the escalation metric requires a concrete requested model to compare against, so it cannot fire by construction under the current trigger config. | Refuted | 0/146 qualify | 146/146 records |
| Perplexity | trust_tiers (domain trust:{level,name,description}) Zero trust blocks anywhere — 69 fully-parsed snapshots plus 450 substring-scanned records spanning Dec 2025–Aug 2026. The field that does exist is a bare domain slug, not a trust tier. | Refuted | 0 occurrences | 519 records checked |
| Perplexity | router_scorecard (classifier_results.mhe_predictions_full) Present and non-empty in every parseable record, exposing the same 17 named intent heads every time — probability/threshold/fired triples exactly as claimed. | Confirmed | 69/69 records | 69 parseable snapshots |
| Perplexity | mode_escalation (search_mode / model swap) The fields are real, but search_mode was null in 100% of records (never STUDY/RESEARCH) and user_selected_model always matched display_model — no escalation observed in this sample. | Partially confirmed | 0 escalations | 69 records |
Frozen snapshot (read-only retest, August 2026) — the live weekly view is drafted but not yet deployed; see Methodology.
Methodology
Source. This is a read-only re-test of open questions from our AEO knowledge base, run against ai-scrapers' own public.fanout_captures table — the same corpus behind the AI Hotel Landscape. Every signal in this article was mined from our own hotel-search captures, not a third party's dataset.
Shape. fanout_captures.raw_response is a JSON-deserialized dict whose response_raw field is (when present) a JSON string containing a list of {event, data} server-sent-event frames. data is itself a JSON-patch-shaped tree (p/o/v/c keys at points) whose values are where fields like supporting_websites, thoughts, classifier_results and search_mode actually live.
Extraction, not path-matching. Because the JSON shape drifts release to release, we walk every dict/list depth-first and collect fields by key presence rather than a hardcoded path — a plain dict.values() traversal still reaches values nested inside the patch tree's v field, confirmed with exploratory probes before writing the main extraction script.
Sampling. ChatGPT: 300 recent captures with non-null raw_response, pulled in 40-row chunks (a single large query reliably hit Supabase statement timeouts and Cloudflare 521/522/525 errors on this project — confirmed empirically), deduped to 150 rows across 150 distinct query_ids, 146 of which parsed cleanly. Perplexity: 150 rows across 101 distinct query IDs (100 oldest-first from Dec 25, 2025; 50 newest-first through Aug 10, 2026), pulled with a two-phase query (lightweight columns first to pick diverse IDs, then a small-batch fetch of the heavy raw_response column — 0.5–2 MB per row), of which 69 parsed into a full snapshot.
Live tracker (pending). A permanent extraction module, fanouts-automation/tasks/raw_signals.py, and a weekly aggregate, public_dashboard.pd_weekly_raw_stream_signals (built via public.rebuild_raw_stream_signals), are drafted to keep the Section 4 table live going forward. The migration is not yet applied — it is pending manual review and deploy — so today's table renders the frozen counts from this retest. Once the migration ships, data lands after the next scheduled refresh and the table above starts updating automatically.
Access. Read-only throughout: only SELECT queries against Supabase, no writes, no migrations applied, no backfills executed. All working scripts and fixtures stayed in a scratch directory and were never committed.
FAQ
Explore the data behind this page
Every number here comes from a read-only walk of our own AI Hotel Landscape captures — CC-BY-4.0.