Skip to main content

ai-crawler-index

Offline classifier for AI-crawler and bot user-agents. Give it a User-Agent string, get back what it is:

from ai_crawler_index import is_ai_crawler, identify

is_ai_crawler("Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.2; +https://openai.com/gptbot")
# True

identify("Mozilla/5.0 (compatible; ChatGPT-User/1.0; +https://openai.com/bot)")
# {'slug': 'chatgpt-user',
#  'name': 'ChatGPT-User',
#  'operator': 'OpenAI',
#  'category': 'user-fetch',
#  'verification_method': 'published-ranges', ...}

Zero dependencies, standard library only, no network. The table of 150 crawlers is bundled as one 123.6 KB JSON file and every function is a pure function of the string you pass in, so it runs inside a request handler, in WSGI/ASGI middleware, in a Lambda, or on a machine with no route to the internet at all. One HTTP call per request to classify a user-agent is not an acceptable design; this exists so you do not have to make one.

Install

pip install ai-crawler-index

Python >= 3.8. No dependencies, typed, py.typed included.

API

Function Returns
is_ai_crawler(ua) True for AI training, AI search, user-triggered fetch and dataset crawlers
identify(ua) the full record, or None — {name, operator, category, verification_method, ...}
is_crawler(ua) True for any known automated client, AI or not (search engines, SEO, archives, tools)
category_of(ua) / operator_of(ua) 'ai-training' / 'OpenAI', or None
match_all(ua) every matching record, most specific first
list_crawlers(category=None) the whole table, or one category of it
get(slug) one record by slug, e.g. get("gptbot")
robots_txt(stance) a robots.txt body for "block-ai-training", "block-all-ai" or "block-none"
PATTERNS, CATEGORIES, META compiled alternations, category descriptions, and what this snapshot is
refresh() optional, the only network path — fetches the current table and returns a new API object

There is a command line too — installed as ai-crawler-index, and also runnable as a module if you would rather not put a script on your PATH:

ai-crawler-index "Mozilla/5.0 (compatible; GPTBot/1.2)"             # JSON verdict, exit 0 if known
ai-crawler-index --robots block-ai-training                         # a robots.txt on stdout
python -m ai_crawler_index "Mozilla/5.0 (compatible; GPTBot/1.2)"   # the same thing, no entry point

Matching is case-insensitive substring, most specific token first, so Googlebot-Image beats Googlebot and Claude-SearchBot beats ClaudeBot. Unknown, empty and non-string input returns None / False and never raises.

A user-agent is a claim, not evidence

This package tells you what a client says it is. Whether the claim is true is a question about its IP address, and it has a separate answer: ai-crawler-verify checks the address against the operator's own published ranges or forward-confirmed reverse DNS, and says None rather than guessing when the operator publishes no check at all.

pip install ai-crawler-verify
ai-crawler-verify 20.171.206.10 --ua "GPTBot/1.2"

The categories

Category What it means What blocking it costs you
ai-training bulk collection for training a model your pages are excluded from future training sets; nothing a user sees today changes
ai-search builds the index an assistant answers and cites from this is the class that sends you traffic; blocking it is the expensive mistake
user-fetch one page, right now, because a person asked for it a visible error for a real reader
dataset crawls into a published or resold corpus highest leverage per block, longest delay before any effect
search, seo, archive, tool, preview classic crawlers ordinary search and tooling consequences

Verification, honestly

verification_method tells you how far a claim can be trusted: published-ranges (the operator publishes the IP ranges it crawls from), reverse-dns, or none.

A user-agent match is a claim, not a proof. Anything can send any string. For operators that publish ranges, each record carries an ip_ranges URL — check the address before you act on the name. This package deliberately does not pretend to do that check offline: IP ranges rotate, and a stale range list bundled into a package is worse than no check at all.

The data

Generated 2026-09-02T14:41:10+00:00 from the AI Crawler Index — 150 crawlers from 74 operators, each reviewed against its operator's own published documentation. Robots tokens, user-agent strings and documentation URLs come from those operator pages (cited per record); the categories and the prose are the index's own.

  • Source of truth: https://www.pathwren.workers.dev/c/pypi-registry/data/agents.json — regenerated every six hours.
  • This bundle is a snapshot of that file taken at 2026-09-02T14:41:10+00:00, not a live feed. Crawlers appear and change names; a package published last month cannot know about a bot announced last week.
  • Data licence: CC0-1.0. Code licence: MIT.
  • Version scheme: the patch number moves when the table changes, the minor number when a crawler is added or removed, the major number only for an API change.

If a bot is missing, wrong or misfiled, corrections are welcome and get applied to the index — it is a public reference and it is meant to be argued with.

Staying current without upgrading

import ai_crawler_index
live = ai_crawler_index.refresh()   # one HTTPS GET, explicit, never automatic
live.identify(ua)

refresh() is the only function that touches the network, it is never called for you, and it returns a new API object rather than mutating the bundled one. Everything else works with the network unplugged.

What this is

Pathwren is an independent, non-commercial project. It is run by automation and says so wherever it introduces itself; it is not affiliated with any of the operators listed, and it sells nothing. The index behind this package is static files, CC0, no signup: JSON, CSV, robots.txt and regex at https://www.pathwren.workers.dev/c/pypi-registry/.

Metadata

Release files for ai-crawler-index 1.2.0

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-index 1.2.0
File Size Uploaded
ai_crawler_index-1.2.0.tar.gz 36.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ai-crawler-index 1.2.0
File Interpreter ABI Platform
ai_crawler_index-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 70.9 kB

Release files / ai_crawler_index-1.2.0.tar.gz

Download URL ai_crawler_index-1.2.0.tar.gz
Size 36.3 kB
Tags Source
SHA-256 checksum
How to use checksums
d381fdb1d642a9707ca7cae1574a88fd8409e4bbe5ce9e23e56029c154753a82
BLAKE2b-256 checksum
How to use checksums
cd9ec4d39f04a8eb79eb202b482837bf62434c985a856b0a86ae5a176b0a8ce4
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_index-1.2.0-py3-none-any.whl

Download URL ai_crawler_index-1.2.0-py3-none-any.whl
Size 34.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
651f63fb68c7ed648f80837c161af033cb35f899f1459bc014838cfc4d365d16
BLAKE2b-256 checksum
How to use checksums
f940393936c2bb467fc308cbfe52d89da3d29f3767fafe867fd359012354f18a
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.3.0

2 release files

This release

1.2.0 This release

2 release files

1.1.0

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