Skip to main content

ai-crawler-robots

A robots.txt that looks right and blocks nothing is the normal failure. This package reads the file you already have and tells you which of the 150 AI crawlers it actually stops.

from ai_crawler_robots import lint, audit, is_allowed

lint(open("robots.txt").read())
# [{'level': 'error', 'line': 3, 'code': 'ua-is-not-a-token',
#   'message': 'User-agent takes a product token, not a full user-agent string. '
#              'This group matches no crawler.',
#   'found': 'Mozilla/5.0 (compatible; GPTBot/1.2; +https://openai.com/gptbot)',
#   'fix': 'User-agent: GPTBot'}]

is_allowed(text, "GPTBot", "/blog/post")
# {'allowed': False, 'rule': 'Disallow: /blog', 'line': 8, 'group': 'GPTBot', ...}

audit(text)["blocked"]        # ['anthropic-claudebot', 'openai-gptbot', ...]
audit(text)["unenforceable"]  # crawlers that will not obey it whatever it says

Install

pip install ai-crawler-robots

Python >= 3.8. Zero dependencies, standard library only. No network at import: 150 crawler records and 8 ready-made stances ship inside the wheel (169.6 KB), snapshot taken 2026-09-12.

Nothing in this package fetches a URL. There is no argument anywhere that takes one. refresh() is the only call that touches the network, you have to name it, and it fetches exactly one document: the same data.json that ships in the wheel.

The faults it finds

The most common robots.txt failure is not a syntax error — it is a file that a person reads as correct and a parser reads as empty. 23 checks, of which these are the ones that cost real traffic:

code what it means
user_agent_is_a_ua_string User-agent: carrying a whole browser-style string. RFC 9309 matches a product token; that group matches nothing.
unknown-token a token no crawler in the table sends — usually a typo, ChatGPT-user for ChatGPT-User, or a name somebody invented
duplicate_group a second group for the same token; only the first one applies, the rest is decoration
noindex_in_robots Noindex: in robots.txt. Google stopped honouring it on 1 September 2019 and it never worked anywhere else.
unenforceable the rule is correct and the operator states robots.txt does not apply to that fetcher. You need an edge rule, and this says so.
crawl_delay Crawl-delay: is not in RFC 9309 and Google, OpenAI and Anthropic all ignore it
path_without_slash Disallow: admin does not match /admin
html_response / empty / bom the file never parsed at all

Every finding carries a line number, what was found, and a fix string you can paste.

Command line

ai-crawler-robots lint robots.txt                       # the faults, worst first
ai-crawler-robots audit robots.txt                      # every AI crawler: blocked / allowed / unenforceable
ai-crawler-robots check robots.txt --agent GPTBot --path /blog/
ai-crawler-robots diff old.txt new.txt                  # what changed by effect, not by text
ai-crawler-robots policy block-ai-training              # print a ready-made stance
ai-crawler-robots policy block-ai-training --merge robots.txt
ai-crawler-robots explain GPTBot                        # who it is, what blocking costs

lint exits 1 when it finds an error, 0 otherwise, so it works as a CI gate:

ai-crawler-robots lint robots.txt --strict || exit 1

audit — the question people actually have

$ ai-crawler-robots audit robots.txt
robots.txt: 150 crawlers, 12 blocked, 39 allowed, 5 unenforceable

BLOCKED (12)
  openai-gptbot            GPTBot              OpenAI       ai-training
  anthropic-claudebot      ClaudeBot           Anthropic    ai-training
  ...
ALLOWED, AND YOU PROBABLY MEANT TO BLOCK (7)
  google-extended          Google-Extended     Google       ai-training
  ...
UNENFORCEABLE BY robots.txt (5)
  These fetch on behalf of a user in a live session. Their operators state
  robots.txt does not govern them. Blocking needs an edge rule.

The third group is the one that surprises people. A user-triggered fetcher is not a crawler, most operators say so in their own documentation, and a Disallow aimed at one is a line that will never fire.

diff — did that edit do anything?

$ ai-crawler-robots diff robots.old robots.new
2 crawler(s) changed verdict for /
  google-extended     allowed -> blocked
  meta-externalagent  allowed -> blocked
44 unchanged. Formatting-only changes are not reported.

Compares by effect. Reordering groups, changing whitespace and rewriting comments produce an empty diff, because none of them change what a crawler does.

Ready-made stances

8 of them, the same bodies published at the index:

  • allow-all — Every crawler on this index is named and allowed. Use when you want maximum reach into search and assistants and have nothing to withhold
  • block-ai-training — Refuse the crawlers that feed model training. Keep the ones that put you in ChatGPT, Claude, Perplexity and Gemini answers
  • block-all-ai — Training, AI search, user-triggered fetches and corpus builders, all refused. Classic search engines still allowed
  • block-datasets — Refuse the crawlers whose output is a dataset other people train on: Common Crawl, AI2, Webz.io, Diffbot, ImagesiftBot
  • allow-ai-search-only — Be findable and citable in assistants without contributing to training corpora
  • block-seo-tools — Ahrefs, Semrush and friends. No user-facing consequence, and often the largest single slice of your bot traffic
  • block-disputed — The ones repeatedly reported as ignoring robots.txt. Included for completeness — expect to enforce this at the edge instead
  • maximum-ai-visibility — Allow every AI crawler and every search engine; refuse only SEO scrapers. For sites whose goal is to be found and cited by machines

--merge adds a stance to an existing file without touching the groups already in it, so your Googlebot rules survive.

Where the data comes from

The AI Crawler Index — an independent, non-commercial public reference. Every crawler record links to the operator's own documentation, and explain prints that link. Presence in the table means the operator documents that crawler; it is not an opinion about whether you should allow it.

refresh() fetches today's table and caches it six hours, which is how often the index behind it is rebuilt:

from ai_crawler_robots import refresh
refresh()   # {'crawlers': 150, 'generated_at': '...'}

Companion packages

Same table, different questions:

  • ai-crawler-index — what does this user-agent claim to be?
  • ai-crawler-verify — is that claim true? Checks the address against the operator's published ranges.
  • ai-crawler-logs — who was actually in your access log, and what to paste to act on it.

Licence

Code MIT. Bundled data CC0-1.0. Independent and non-commercial; not affiliated with, endorsed by or speaking for any crawler operator named in the table.

Metadata

Release files for ai-crawler-robots 1.0.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ai-crawler-robots 1.0.8
File Size Uploaded
ai_crawler_robots-1.0.8.tar.gz 52.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ai-crawler-robots 1.0.8
File Interpreter ABI Platform
ai_crawler_robots-1.0.8-py3-none-any.whl Python 3 none any Details

Total release size: 103.2 kB

Release files / ai_crawler_robots-1.0.8.tar.gz

Download URL ai_crawler_robots-1.0.8.tar.gz
Size 52.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1b368c05fcbaad5394fac51c988b9bf606b4f36daddaa8a65d6565478a3702bd
BLAKE2b-256 checksum
How to use checksums
89d6f4007124989010f6e005b1c899492e4fa177c75f5529fc18d0b6ca9b90c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release files / ai_crawler_robots-1.0.8-py3-none-any.whl

Download URL ai_crawler_robots-1.0.8-py3-none-any.whl
Size 50.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
da5bf6c94b627bec1ca0bd5fefe752df5151c267aca3ae5481a1009c740edf04
BLAKE2b-256 checksum
How to use checksums
1cb8079d22e9d1ccdedac66373a5cac909c10d54f01c03a2e6bd7ba58e92c72d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release history Release notifications | RSS feed

1.0.10

2 release files

1.0.9

2 release files

This release

1.0.8 This release

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release 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