Skip to main content

geo-check

On the GitHub Marketplace On PyPI CI status MIT licence Python 3.10 or newer Audited across 906 sites No LLM calls

Check whether a website is reachable and readable by AI crawlers, and score it.
Two scores, never averaged. Blocking model training costs no points.

geo-check auditing nytimes.com: Access 67.23 out of 100 grade C, Readability 82.5 out of 100 grade B, 5 of 11 citation crawlers allowed, and the exact robots.txt lines to add.

A real run, replayed from a recording committed to this repository. Trimmed for height.

Try it

As a Claude Code skill. The SKILL.md at the root of this repository is the skill, and one command installs it:

npx skills add vasco-branco06/geo-check

As a command line tool, which is also what the skill runs:

pip install geo-check
geo-check your-domain.com

Python 3.10 or newer and open network access. No API key, no account, no model call. It does not run inside sandboxes that only reach an allowlist of domains.

Or run it without installing anything: fork this repository, open the Actions tab, choose audit a site and press Run. The report lands in the run summary.

What using it looks like

Someone says the company is not showing up in ChatGPT. Nobody knows whether that is a marketing problem or a robots.txt problem, and those have different owners.

Ask Claude Code, in whatever words:

Is nytimes.com blocked from AI search?

The skill runs the audit and answers with its reason. On nytimes.com today that reason is, trimmed only of the matched rule it prints beside each name:

5 of 11 citation crawlers allowed. Blocked: OAI-SearchBot, Claude-SearchBot, PerplexityBot, Meta-WebIndexer, DuckAssistBot, YouBot. Every crawler that exists only to feed AI answers is blocked, while classic search crawlers still get through. The site is reachable by Google and Bing and invisible to ChatGPT, Claude and Perplexity.

That is the finding this tool exists to produce, and it is not one a person spots by reading the file. Those six lines sit beside GPTBot, which is training and costs nothing, and beside Googlebot, which is allowed. Eighteen of the 906 sites in the validation corpus are in exactly this state.

You get the two scores, which of the 32 documented crawlers are blocked and by which line, and the robots.txt to paste. You do not get a model's opinion, because no model is in the scoring path.

What GEO is, and why it matters

Generative Engine Optimization. The question is no longer only whether Google lists you, it is whether ChatGPT, Claude, Perplexity and AI Overviews can find you, read you, and quote you.

Most advice treats AI bots as one thing. They are not, and the confusion is expensive. OpenAI alone runs three crawlers that do different jobs:

Crawler What it does Blocking it costs you
GPTBot Collects pages for model training Nothing in ChatGPT results
OAI-SearchBot Decides whether ChatGPT can cite you Your place in ChatGPT results, entirely
ChatGPT-User Fetches a page someone asked about Nothing, it is documented as ignoring robots.txt

Those first two sit one line apart in a robots.txt file. Anthropic, Perplexity, Google and Apple all split their crawlers the same way.

Some sites block the first on purpose and say so in public. Others opt out of training and remove themselves from ChatGPT at the same time without meaning to, and from outside a robots.txt file you cannot tell which is which. What you can tell is exactly which lines are there and what each one costs, which is what this prints.

What makes this one different

Two scores, never averaged. excalidraw.com scores 100 on Access and 32 on Readability. It is wide open to every crawler and unreadable by all of them. An average of 66 would describe nothing true about that site and would hide the one thing worth acting on, so the two never collapse into one figure.

Training crawlers are worth zero points, on purpose. Blocking model training is a legitimate business decision, not a mistake. A newspaper protecting its archive has done nothing wrong, and a tool that deducts points for it is not measuring, it is lobbying. Training appears as one informational line and never touches either score.

Nothing is scored by a model. No LLM sits in the scoring path. Every point comes from a rule you can read in docs/RUBRIC.md and trace to the line of robots.txt or markup that earned it, so the same site scores the same twice and a score you disagree with is an argument you can have.

Validated against 906 real sites, and the validation found bugs here first. 744 scored, zero crashes, and no abort without a reason. Every robots.txt was then read three ways and compared per agent, 21792 verdicts, agreeing 100 percent with an independent reader written from RFC 9309. Three implementations agreeing proves consistency and not correctness, so the verdicts that carry a score get read by a person: 289 of them have been, and docs/VALIDATION.md says which.

It caught three real defects in this tool before anyone else could:

Defect What it would have done
User agents matched by substring A site writing User-agent: bot would have silently blocked six citation crawlers at once
Groups declaring the same agent not merged A site that contradicts itself got the first rule instead of the merge RFC 9309 requires
A bare Crawl-delay closing nothing One site's CCBot inherited a Disallow aimed at a marketing crawler

The 342 MB of recordings behind that are too large to ship, so data/corpus_manifest.csv carries the SHA-256 of every robots.txt as it was read, all 906 rows, dated. python scripts/verify_manifest.py --sample 25 fetches them today and tells you which still match. The evidence, and what it does not prove, is in docs/VALIDATION.md.

Keep it from regressing

A site is one deploy away from losing its AI visibility, and nobody notices for months. Add this to any workflow and a bad robots.txt fails the build:

- uses: vasco-branco06/geo-check@v0
  with:
    domain: your-domain.com
    fail-under-access: 60
    fail-under-readability: 50

Both thresholds are optional. Leave them out and it reports without failing. The full report goes into the job summary, and the scores come back as outputs you can read in later steps.

It is on the GitHub Marketplace.

The two scores

Access is whether AI systems can reach the site at all. Citation crawlers allowed (50), user fetch crawlers allowed (20), sampled pages reachable without a login wall (15), sitemap declared and reachable (10), no noindex (5).

Readability is whether an AI system can make sense of a page once it has it. Content in the raw HTML without JavaScript (30), valid JSON-LD (20), heading structure (15), title and meta description (10), author and dates (10), canonical (5), llms.txt (5), answer shaped content (5).

Grades: 90 and above A, 75 to 89 B, 60 to 74 C, 40 to 59 D, below 40 F.

Three critical failures cap the Access score. All citation crawlers blocked caps it at 20. A blanket Disallow: / caps it at 10. A homepage that does not return 200 aborts the run, because there is nothing honest to score.

The full rubric, with the reasoning and the weaknesses it still has, is in docs/RUBRIC.md.

Questions people actually ask

Isn't this just reading robots.txt? That is one of the thirteen checks, and it is the one most tools get wrong. Matching a crawler to a group is a specification, not a substring search, and following RFC 9309 properly is where all three bugs found here lived. The other twelve read the sitemap, llms.txt, and five sampled pages for structure, JSON-LD, headings, authorship and whether the content survives without JavaScript.

Why doesn't blocking model training cost points? Because it is a decision, not a defect. Plenty of publishers block training deliberately and are right to. A tool that deducts for it has an opinion about your business model rather than a measurement of your site. It is reported as one line so you can see it, and it moves neither score.

Why two scores instead of one? Because they fail independently and different people fix them. A site can be perfectly reachable and impossible to read, or beautifully structured and blocked at the first line of robots.txt. One number hides whichever half is broken.

Does it work if I am behind Cloudflare? It reads what your robots.txt and markup say. It cannot see a bot manager returning 403 to crawlers while serving you fine, and it says so in the report rather than scoring around it. If the homepage refuses this tool, the run aborts with the reason instead of inventing a score.

Why trust a number a heuristic produced? Only trust the ones that are measured, and the report tells you which is which. The JavaScript check is a heuristic with thresholds taken from 22 live pages, and docs/RUBRIC.md names the page in that sample it gets wrong. The robots.txt verdicts are not a heuristic, and they carry most of the Access score.

Do I need llms.txt? Probably not urgently. It is a proposal rather than a standard and no major assistant is documented as requiring it, which is why it is worth 5 points out of 100 here and not more.

What this does not do

It does not detect blocking at the CDN or WAF level. Your robots.txt can say yes while Cloudflare returns 403 to AI crawlers, and this tool will not see it.

It does not render JavaScript. The raw HTML check measures how much readable text arrives in the initial response and how much of that response is script. The thresholds were measured across 22 live pages rather than chosen, and both the numbers and the script that produced them are in the repository.

Answer shaped content is detected structurally. The check can see that a page has an ordered list of five steps and that the prose is broken into sections. It cannot see whether either is any good.

It does not measure whether AI assistants actually cite you. That is a different and much larger problem.

The crawler list

32 user agents, each with its vendor, its bucket, whether the vendor documents it as honouring robots.txt, and a link to that documentation. docs/CRAWLERS.md explains how the buckets work and why six of the on demand fetchers are reported as ignoring a block aimed at them.

The list moves. Pull requests adding or correcting an entry, with the vendor's own documentation URL, are the most useful contribution you can make.

Contributing

Adding a check is one file in src/geo_check/checks/ and one line in the registry.

pip install -e ".[dev]" && pytest

189 tests, offline, under half a minute, covering 91 percent of the package. The suite never touches the network, and CI prints the coverage on every run. CONTRIBUTING.md has the rest: how a check is written, how the golden set works, how to record fixtures, and why the recorder is deliberately slow. SECURITY.md carries one known and unfixed issue worth reading before you pipe this tool's output into an agent.

Licence

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

geo_check-0.4.0.tar.gz (225.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

geo_check-0.4.0-py3-none-any.whl (67.1 kB view details)

Uploaded Python 3

File details

Details for the file geo_check-0.4.0.tar.gz.

File metadata

  • Download URL: geo_check-0.4.0.tar.gz
  • Upload date:
  • Size: 225.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for geo_check-0.4.0.tar.gz
Algorithm Hash digest
SHA256 0bea93dc8cabe755b660459d6d57cd49f26970942a329f52fa94186cddd3a25a
MD5 b15505a8493587e3d4ee0dc6709c016b
BLAKE2b-256 cb795cb430e513a01c583996cb74e1101db84d639879248b995a71e1641059b0

See more details on using hashes here.

Provenance

The following attestation bundles were made for geo_check-0.4.0.tar.gz:

Publisher: release.yml on vasco-branco06/geo-check

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file geo_check-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: geo_check-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 67.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for geo_check-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f464600d0694cd43fa8812aab272713996f5be0e8540810ca898a4f493ad6024
MD5 278ea7b1a2146c12c2a71c7767230abd
BLAKE2b-256 ed8d47feebb39bf5db614878093f9a336b77444a522d5db40eccad4e4a753324

See more details on using hashes here.

Provenance

The following attestation bundles were made for geo_check-0.4.0-py3-none-any.whl:

Publisher: release.yml on vasco-branco06/geo-check

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

0.3.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page