# What we check

All 25 checks and every weight, published, so you can argue with the score instead of taking our word for it.

Source: https://www.botready.dev/what-we-check
Updated: 2026-09-09

A category is worth its weight out of 100. The checks inside it split that weight between them, and the number
beside each check below is exactly what failing it takes off the score — the same number the findings list on a
result prints back. A pass earns all of it, a warn earns half, a fail and an error earn none, and a check we
could not run leaves the denominator rather than counting as a zero.

## The clients

- `chrome` (control) — `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36`
- `claudebot` (agent) — `ClaudeBot/1.0`
- `gptbot` (agent) — `GPTBot/1.2`
- `perplexity` (agent) — `PerplexityBot/1.0`
- `googleext` (agent) — `Google-Extended`

## The checks

### Retrievability — 25 of the 100

Nothing else can matter if the client cannot get the page. This carries the most weight because every other category is measured on text that was retrieved, and a refusal makes the rest unobservable rather than merely worse.

#### Every client gets the same answer (`agent_status_parity`) — 12.9 of the 100

The headline finding of the whole product: 200 to Chrome and 403 to ClaudeBot, same URL, same second. The largest single check in the catalog, because it is the only failure that makes every other measurement moot.

Fails when any agent client receives a different status class than the Chrome control.

#### Page text survives without JavaScript (`js_dependency_ratio`) — 7.9 of the 100

Text that appears only after JavaScript runs is text most agent clients never see. Second largest because it is the failure that looks perfect in a browser and is invisible to the people it costs you.

Fails when ratio above 0.7.

Warns when ratio between 0.4 and 0.7.

#### First byte arrives quickly (`raw_fetch_latency`) — 2.1 of the 100

A slow first byte gets the request abandoned. Small because real timeouts are rare, and because this is the noisiest measurement in the catalog — see S2 in SCORING-PLAN.md.

Fails when ttfb above 2500 ms.

#### No long redirect chains (`redirect_depth`) — 2.1 of the 100

Every hop is a chance to lose the client. Small because chains are uncommon and a short one is harmless.

Fails when more than 3 hops.

### Discovery — 20 of the 100

The files a client asks for before it reads anything: robots.txt, a sitemap, llms.txt. Weighted high for their cost — they are the cheapest things on this list to fix and they decide what gets fetched next.

#### robots.txt exists and parses (`robots_present`) — 3.1 of the 100

Not a refusal in itself, but it is the first thing every client asks for, and a 5xx here stops some of them before they reach the page.

#### robots.txt does not silently block reading agents (`robots_agent_rules`) — 6.2 of the 100

A robots.txt that disallows reading agents is a refusal you chose, usually by pasting a rule nobody read. Worth twice robots_present because this one actively turns clients away rather than merely failing to help them.

Fails when an agent user agent is disallowed while search crawlers are allowed.

#### Sitemap exists and is not empty (`sitemap_present`) — 3.1 of the 100

How a client finds the pages you did not link from the front door.

#### llms.txt tells agents which pages matter (`llms_txt_present`) — 3.8 of the 100

The file agents look for to learn what you do in plain language. Just above the sitemap because it is the one file here we can generate for you in full, from pages we watched return 200.

#### A second page is reachable without a browser (`pages_reachable`) — 3.8 of the 100

A site an agent can only see one page of is a site an agent cannot answer questions about. Fails when the HTML you send carries no link to another page of your own site and the sitemap offers none either — most often because the navigation is built by JavaScript, which means a client that does not run one arrives at your front door and finds no doors.

Fails when no page beyond the target could be reached from the HTML as sent, and the cause is the site rather than our own cap or a request nothing answered.

Warns when your robots.txt disallows the pages we would otherwise have read.

### Representation — 20 of the 100

Whether your own account of what you do survives as data rather than as layout. An agent can only quote what it can extract.

#### A markdown representation is advertised (`markdown_alternate`) — 7 of the 100

A markdown representation advertised in a Link header or tag is the cheapest way to hand a client clean prose instead of a layout to unpick.

Fails when no link rel=alternate type=text/markdown and no Link header equivalent.

#### One h1, a sane heading order, a main landmark (`semantic_landmarks`) — 6 of the 100

One h1, a sane heading order and a main landmark are how an extractor decides which part of the page is the content.

#### Each page has its own title and description (`title_meta_distinct`) — 4 of the 100

Pages that share a title are pages a client cannot tell apart when it comes to cite one of them.

#### Server honours Accept: text/markdown (`content_negotiation`) — 3 of the 100

Honouring Accept: text/markdown is the polite version of the markdown alternate. Small because hardly anyone implements it and its absence is unremarkable.

### Structure — 15 of the 100

The scaffolding that says which page this is and what it is about: headings, canonical URLs, JSON-LD. Below representation because a well-described page with untidy structure still gets read.

#### JSON-LD describes what this is (`jsonld_present`) — 6 of the 100

On most pages this is the only place the site states what it is in a form that needs no interpretation.

#### Pricing is machine readable (`pricing_structured`) — 5 of the 100

An agent quotes a price it can read as data. A price in a table is a picture of a price.

Fails when prices appear only in images or JS-rendered tables.

#### Canonical URL and OpenGraph are complete (`canonical_og`) — 4 of the 100

Which URL is the real one, and what the page says about itself when somebody shares it.

### Actionability — 15 of the 100

Prices, plans, docs and forms an agent can act on rather than only describe. Equal to structure because being quotable and being usable are worth the same to a buyer.

#### An agent manifest or WebMCP endpoint exists (`agent_manifest`) — 2.9 of the 100

A manifest or WebMCP endpoint turns a page an agent can read into a service an agent can use. Scored knowing most sites will fail it for a while yet; this is the check most likely to move at the next calibration.

#### API docs are two hops from the homepage (`api_docs_reachable`) — 2.3 of the 100

Docs two hops from the homepage are docs an agent finds while it is answering a question about you.

#### Primary forms have labels, names and autocomplete tokens (`form_semantics`) — 2.3 of the 100

Labels, names and autocomplete tokens are what let an agent complete a form rather than describe one.

#### Docs and pricing are not behind auth or a captcha (`no_wall_on_docs`) — 1.2 of the 100

Docs behind a login are docs no client reads. Small because it is rare, and because when it is deliberate it is a legitimate choice we should not punish hard.

#### A person is reachable in a way an agent can hand over (`contact_reachable`) — 2.3 of the 100

The most common thing anybody asks an assistant about a business is how to reach it. A tel: or mailto: link, or a telephone in your JSON-LD, is something an agent can read out or dial. A phone number set as an image, or as text with no link, is a number it cannot pass on. Every business has this answer already; this check is about whether it is machine-readable.

#### What can be done here is declared, not just described (`action_declared`) — 2.3 of the 100

Booking a table, ordering, requesting a quote, subscribing: a schema.org Action or an Offer says which of those your site supports. Without one an agent can describe you and cannot act for you, which is the difference the whole category is measuring. This is the check most sites can pass with one block of JSON-LD.

#### The way to act is in the HTML, not only after JavaScript (`action_not_js_only`) — 1.7 of the 100

A booking button that only exists once a script has run is a button no non-rendering client can find. Measured by comparing the raw response against the rendered DOM: contact links or declared actions present in one and not the other.

### Freshness — 5 of the 100

Dates and change signals. Worth the least because a stale signal loses a citation rarely, and it is the category where a maintained site can look bad for reasons that do not matter.

#### Last-Modified or ETag is sent (`cache_headers`) — 3 of the 100

Last-Modified or ETag tells a client whether reading you again is worth it.

#### Sitemap lastmod values are maintained (`sitemap_lastmod_real`) — 2 of the 100

A lastmod that never changes is worse than no lastmod at all, because it is a claim rather than a gap.

Fails when every lastmod is identical or in the future.

## On the weights

They are our estimates. They have not yet been measured against whether a site actually gets cited, we are
collecting that evidence now, and we will publish it alongside whatever it says about these numbers before we
change them. Changing any of them is a versioned event: every score records the version that produced it.
