---
title: Was This the Page You Asked For? Six Fields That Answer It — NanoParse Blog
url: https://nanoparse.app/blog/2026-10-19-page-you-asked-for
description: A parse returns Markdown. Beside it sit twenty signal groups, and six of them answer a different question than
---

A parse returns Markdown. Beside it sit twenty signal groups, and six of them answer a different question than "what does this page say": what is this document, who stands behind it, and did the fetch land where it was pointed.

The whole set is documented at [nanoparse.app/litmus](https://nanoparse.app/litmus) and in the schema at [nanoparse.app/openapi.json](https://nanoparse.app/openapi.json). What follows is the six, run against real pages on 2026-10-19, with the empty results left in. Twenty-five URLs were submitted to the live endpoint through the admin research path, so the run billed nothing. Fifteen returned a document, covering ten distinct pages. Ten were refused with a named error code. Every payload is committed beside this post.

Price is unchanged and lives in one place: $0.005 per successful parse, refused reads settle nothing.

## The six, as returned

Every parse carries all twenty groups, including the ones that found nothing. This is the tail of a real response for `https://en.wikipedia.org/wiki/Web_scraping`, unedited:

```
{
"authorship": {
"present": true,
"name": "Contributors to Wikimedia projects",
"kind": "person",
"source": "jsonld"
},
"page_status": { "is_error_page": false, "kind": null, "confidence": "high" },
"citations": {
"has_references": true,
"external_links": 109,
"external_per_1k_words": 26.3,
"primary_domain_links": 1
},
"structured_data": {
"json_ld": true,
"schema_types": ["Article"],
"open_graph": true,
"canonical": "https://en.wikipedia.org/wiki/Web_scraping",
"meta_description": false
},
"quote_density": { "attributed_quotes": 60, "attribution_verbs": 2, "per_1k_words": 14.5 },
"redirects": {
"final_url": "https://en.wikipedia.org/wiki/Web_scraping",
"hops": 0,
"cross_domain": false
}
}
```

All six are computed from data the render already produced — the HTML, the Markdown, the final URL. No second model call, no classifier, no per-signal charge.

## The run, as numbers

Word counts below are the returned Markdown split on whitespace. `cit` is external links and external links per 1,000 words. `q/v` is quotation marks and attribution verbs.

```
page words author cit/1k jsonld q/v
en.wikipedia.org/wiki/Web_scraping 4156 Contributors…(jsonld) 26.3 1 60/2
rfc-editor.org/rfc/rfc9110.html 66479 Roy T. Fielding (meta) 0.5 0 333/24
www.nasa.gov/ 481 Dacia Massengill (jsonld) 235.9 7 0/0
www.bbc.com/news 1374 none 11.1 1 3/10
arxiv.org/abs/1706.03762 693 none 67.1 0 0/0
developer.mozilla.org/…/Fetch_API 470 none 45.5 0 0/0
developer.mozilla.org/…/Window/fetch 806 none 30.1 0 9/0
www.postgresql.org/docs/…/sql-select 10715 none 0.0 0 0/0
www.iana.org/help/example-domains 125 none 32.8 0 0/0
example.com 135 none 7.6 0 0/0
```

Three of ten pages carried a name. Every page carried all twenty groups.

## authorship: three sources, in order

The field looks in three places and stops at the first hit: JSON-LD `author`, then or `article:author`, then a `By …` line in the first 300 characters of Markdown. The reported `source` says which one fired.

The run shows all three. Wikipedia and NASA answered from JSON-LD. RFC 9110 answered from a meta tag, `Roy T. Fielding`. No page needed the Markdown byline fallback.

The kind classifier is a rule of thumb, and it is worth knowing before trusting it. A name containing an organisation word — inc, llc, corp, group, team, editorial, news, press, agency, wire, network — is an organisation. A single token with no space is a masthead, so `Reuters` reads as an organisation. Everything else with a space is a person. That is why Wikipedia's `Contributors to Wikimedia projects` comes back as `kind: "person"`: it has spaces and no organisation word. The name is right, the kind is wrong, and the payload shows enough to see it.

The other failure is silence. arXiv, MDN, the PostgreSQL manual and the BBC news index all returned `present: false` — no author under any of the three rules, on pages that carry bylines and contributors in markup the rules do not read. `present: false` means "not found by these three rules", not "nobody wrote this".

## page_status: the flag that never fired

`page_status` asks whether the document that came back is the document that was asked for. It reads the title and the rendered Markdown — not the raw HTML — and looks for captcha prompts, login walls, "enable JavaScript" shells, and 404-shaped short pages.

It fired on none of the ten pages. That is the correct result for those ten, and it is also the least interesting fact about the field. The interesting part is who else was in the run: the ten refusals returned no signal object at all. A GitHub request and a Cloudflare Blog request came back `blocked` with `blocked_by: consent_wall:markup` and HTTP 403. A DOI resolver came back `blocked` on a Cloudflare challenge. Hacker News and x.com came back `content_insufficient` — 71 and 32 words, a stub. Instagram was refused at submission, before a job existed, with a domain-level block naming a JS shell or login wall.

So the wall is usually caught upstream, by the refusal path that names its reason and charges nothing. `page_status` is the last line, for the case where a wall slips through as a 200 with plausible prose — a soft 404 that says "page not found" in a real template, a login prompt rendered inside an article layout.

One detail from the source is worth carrying: the wall detector originally scanned raw HTML and false-positived on clean pages whose scripts happened to mention captcha machinery. It now scans only the title and the visible Markdown. That is the right scope for the question — a page that discusses captchas is not a captcha — but it also means a wall whose phrases are hidden in an overlay the renderer stripped will not be seen.

## citations: link shape, not sourcing quality

`citations` counts external `href`s in the HTML, drops self-links and asset URLs, counts links to `.gov`, `.edu`, `.mil`, `.int` and journal domains as primary, and reports the density per 1,000 words. `has_references` is separate: it looks for a Markdown heading named References, Bibliography, Sources, Citations, Further Reading or Works Cited.

The range across ten pages is the whole story: 0.0 external links per 1,000 words on the PostgreSQL `SELECT` manual, 235.9 on the NASA home page.

```
www.postgresql.org/docs/…/sql-select 0 external, 0 primary, 10715 words
www.nasa.gov/ 113 external, 106 primary, 481 words
en.wikipedia.org/wiki/Web_scraping 109 external, 1 primary, 4156 words
arxiv.org/abs/1706.03762 46 external, 1 primary, 693 words
```

The NASA figure is not a research apparatus. It is a 481-word home page carrying 113 external links, 106 of them to primary-domain hosts in its own `.gov` estate, and the per-1,000-words denominator makes a link-heavy navigation bar look like a citation machine. The PostgreSQL manual is the mirror image: a long, self-contained reference page that cites nothing outside itself, so its density is zero and its `has_references` is false even though it is the more authoritative document.

Read the field as a description of link shape. A high `primary_domain_links` count says the page points at primary sources; it does not say the prose uses them. `has_references` says a references section exists by heading, not that the claims in the body map onto it.

## structured_data: what the publisher declared

Four booleans and a list: whether valid JSON-LD parsed, which `@type` values it carried (following `@graph` and `mainEntity`), whether Open Graph tags exist, whether a canonical link is present, whether a meta description is.

Three of ten pages parsed JSON-LD. NASA's home page declared seven types — `Organization`, `WebSite`, `ImageObject`, `WebPage`, `Person`, `Article`, `VideoObject`. Wikipedia declared `Article`. BBC's news index declared `WebPage`. MDN, arXiv, RFC 9110, the IANA help page, the PostgreSQL manual and example.com declared none.

`canonical` is the most quietly useful value in the group, because it is the page's own statement of where it lives — and it does not always match the URL that was requested.

## quote_density: two numbers that must be read together

The counter finds quoted spans of three characters or more (straight and curly), skips fenced code and indented blocks, then counts attribution verbs — said, says, according to, told, stated, reported, claimed, noted, added, wrote, explained, argued — anywhere in the text. `per_1k_words` is quotes per thousand words.

Read alone, the quote count misleads. Wikipedia's `60` quotations against `2` attribution verbs is not sixty sourced quotes; it is sixty pairs of quotation marks in an encyclopaedia that puts titles, terms and translation glosses in quotes, plus a references apparatus. RFC 9110's `333` against `24` is the same shape at specification scale — quoted grammar, not quoted people. BBC's news index is the one page where verbs outnumber quotes, 10 to 3, which is what an index of headlines looks like: attributions without the words attributed.

The pair is the signal. Quotation marks with almost no attribution verbs say "this text quotes things"; verbs without quotes say "this text reports speech without quoting it". Neither number, on its own, says the page is well sourced.

## redirects: the field to read alongside canonical

`redirects` reports the final URL, a hop count, and whether the landing domain differs from the requested one. In this run it reported `hops: 0` and `final_url` equal to the requested URL on every one of the fifteen renders, including three where the page's own canonical said it had landed somewhere else:

```
requested https://developer.mozilla.org/en-US/docs/Web/API/Fetch
canonical https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch
reported final_url = .../Web/API/Fetch, hops = 0

requested https://en.wikipedia.org/wiki/web_scraping
canonical https://en.wikipedia.org/wiki/Web_scraping
reported final_url = .../wiki/web_scraping, hops = 0
```

The source explains the shape. Hops are counted from 3xx responses the browser render observes; pages served through the direct-fetch path return only the HTML, so the response's own final URL is used for an SSRF check and is not carried into the payload. When no final URL reaches the signal, it falls back to the requested URL with zero hops.

The practical rule: treat `redirects` as a report of what the fetch layer counted, and cross-check `structured_data.canonical` when the exact URL matters — for citation, for deduplication, for a link a reader will click.

## Branching on the six

A gate that decides whether a page is citable can be written from these fields without a model call. This one is Python and reads a completed job from the status endpoint:

```
import json, urllib.request

def citable(job):
lit = job["litmus"]
md = job["markdown"]
reasons = []

if lit["page_status"]["is_error_page"]:
reasons.append(f"error page: {lit['page_status']['kind']}")
if len(md.split()) < 200:
reasons.append("under 200 words")
if not lit["authorship"]["present"]:
reasons.append("no author signal")
if not lit["structured_data"]["canonical"]:
reasons.append("no canonical URL")
if lit["citations"]["external_per_1k_words"] == 0:
reasons.append("no external citations")

return (not reasons), reasons
```

Two of those checks would fail a good document. The PostgreSQL manual has no author signal, no canonical and no external citations, and it is a better source than the NASA home page, which passes the citation check by linking to itself 106 times. That is the honest shape of heuristics: they narrow the set, they do not settle it. The signal that survives contact with real pages is the one that says the document is wrong — `page_status`, a named refusal, a 404 — not the one that says it is good.

## What the six do not do

None of them reads the prose. `authorship` matches declarations in metadata and one byline pattern; it does not recognise an author named in body text. `page_status` matches wall phrases in the title and Markdown; it does not detect a wall drawn by an overlay the renderer discarded. `citations` counts anchors; it does not check that a link supports the sentence citing it. `structured_data` reports what the publisher claimed, including claims that are wrong. `quote_density` counts punctuation and verbs. `redirects` reports the hops the fetch layer observed.

No verdict field ships, and none of the six is a score. They are descriptions, each with a stated rule, returned next to the text so a caller can apply its own threshold. Twenty groups, one request, $0.005 when the read succeeds and nothing when it does not — the same envelope whether the caller is a curl loop or the hosted endpoint at [nanoparse.app/mcp](https://nanoparse.app/mcp).

For the earlier runs this one builds on: [Fifteen Signals, Measured](https://nanoparse.app/blog/2026-10-01-litmus-signals-measured) covers the values of the original set, and [What Survives the Convert](https://nanoparse.app/blog/2026-10-07-what-survives-the-convert) covers what the Markdown keeps.
