# AI answer-engine citation audit — dataset

Companion data for the Haus Research report *We Checked Every Citation
Perplexity Gave Us* (HR-2026-09), published 2 September 2026.

Licence: **CC BY 4.0**. Attribution: Haus Research, https://hausresearch.com.

## How this was collected

310 factual questions about 210 technology companies were put to
`perplexity/sonar`, `perplexity/sonar-pro` and `openai/gpt-4.1:online` through
OpenRouter at temperature 0 on 2 September 2026. Every URL either model cited
was then fetched once and classified. Where both Perplexity models marked a
claim inline as `[n]`, that sentence was bound to the exact URL the marker
indexes, and the cited page was checked for the claim's own figures.

`METHOD.md` in this directory is the full description, including the
classification rules, the retry passes and the exclusions. The scripts that
produced every file here are in `scripts/`, in the order they were run:
`gen_questions.py`, `ask.py`, `retry.py`, `fetch.py`, `refetch.py`,
`proxy_refetch.py`, `pairs.py`, `judge.py`, `judge_topup.py`, `wayback.py`,
`verify_examples.py`, `analyze.py`, `export.py`.

Fetches were made with an identifying User-Agent
(`HausResearchBot/1.0`, contact address included), at concurrency 20 with a
10-second timeout, and — for pages that did not open on the first attempt —
through a rotating proxy gateway. The proxy credentials are read from a
`HAUS_PROXY` environment variable and appear in no file here. The same is true
of the OpenRouter key, which `ask.py` and `judge.py` read from
`OPENROUTER_API_KEY`.

## Files

### `questions.csv` — 310 rows

| Column | Meaning |
|---|---|
| `qid` | Question id, used as the join key everywhere else |
| `company` | Company the question is about |
| `topic` | One of founding, funding, headcount, pricing, hq, revenue, security, ceo, acquisitions, sla |
| `question` | The question as sent |

### `citations.csv` — one row per unique cited URL per engine

| Column | Meaning |
|---|---|
| `engine` | Which model cited it |
| `url` | The URL exactly as cited |
| `host` | Hostname, `www.` stripped |
| `http_status` | HTTP status, or a negative code for a network failure (-2 TLS, -3 connection/DNS, -4 timeout, -9 other) |
| `final_url` | URL after redirects |
| `redirects` | Number of redirect hops |
| `class` | `live`, `gated`, `dead`, `empty` or `unreachable` — defined in METHOD.md §3 |
| `soft_404` | Page returned 200 but reads as "not found" |
| `bot_wall` | Bot interstitial detected |
| `paywall` | Subscription wording detected |
| `login_wall` | Sign-in wording detected |
| `visible_text_chars` | Length of the page's visible text |
| `redirected_to_homepage` | A deep link that landed on the site root |
| `cross_domain` | Redirected to a different registrable domain |
| `fetched_via_proxy` | This row's result came from the proxy retry pass |

### `claim_citations.csv` — one row per claim–citation pair

| Column | Meaning |
|---|---|
| `engine` | Which model made the claim |
| `qid`, `company`, `topic` | Join keys back to `questions.csv` |
| `claim` | The sentence, with its `[n]` markers stripped |
| `url`, `host` | The URL that sentence's marker indexed |
| `page_class` | The class from `citations.csv` |
| `claim_numbers` | The claim's specifics, pipe-separated |
| `numbers_found_on_page` | How many of them appear in the cited page's text (blank where the page was not readable) |
| `deterministic_pass` | `True` when the page is live and contains at least one of them — the report's headline metric |
| `llm_verdict` | `SUPPORTED`, `PARTIAL` or `UNSUPPORTED` from the secondary judge, blank if not sampled |
| `llm_reason` | The judge's one-line reason |

`openai/gpt-4.1:online` emits no inline markers, so it contributes rows to
`citations.csv` only.

### `wayback.csv` — archivability of a random sample of cited URLs

| Column | Meaning |
|---|---|
| `url` | The cited URL |
| `cdx_resolved` | Whether the CDX index answered within three attempts |
| `ever_archived` | Whether the Wayback Machine holds any capture of it |
| `captures` | Number of captures returned, collapsed to one per year |
| `first_capture`, `last_capture` | Timestamps, `YYYYMMDDhhmmss` |

### `numbers.json`

Every figure quoted in the report, as computed by `scripts/analyze.py` from the
files above. No number in the report was typed by hand.

### `evidence/`

For each site named in the report, the page as re-fetched on the day of
writing: URL, timestamp, HTTP status, whether each quoted string is present in
its visible text, and that visible text. `evidence.json` is the same in machine
form.

## What is not here

Page bodies for the full crawl. `citations.csv` records how each page resolved
and how much text it had, not the text itself, which keeps the dataset small and
avoids republishing other people's content.
