Skip to main content

ai-crawler-verify

A user-agent is a claim, not evidence. Anyone can send Mozilla/5.0 (compatible; GPTBot/1.2; +https://openai.com/gptbot). The operators that mean it publish a way to check — an IP-range file, or a forward-confirmed reverse-DNS suffix. This package runs that check.

from ai_crawler_verify import verify

verify("132.196.86.1", ua="Mozilla/5.0 (compatible; GPTBot/1.2; +https://openai.com/gptbot)")
# {'verified': True,
#  'method': 'published-ranges',
#  'operator': 'OpenAI',
#  'matched_prefix': '132.196.86.0/24',
#  'reason': "address is inside 132.196.86.0/24, published by OpenAI at https://openai.com/gptbot.json", ...}

verify("203.0.113.9", ua="GPTBot/1.2")
# {'verified': False, 'method': 'published-ranges',
#  'reason': 'address is in no prefix OpenAI publishes for GPTBot', ...}

Three verdicts, and they are deliberately different things:

verified meaning
True the operator publishes a check and this client passes it
False the operator publishes a check and this client fails it — the claim is forged
None nobody publishes a check for this claim, so no verdict is possible and none is invented

That third row is the point. Of the 56 crawlers in the table, 15 are verifiable from published IP ranges and 9 by reverse DNS; for the rest there is nothing to check, and a package that returned False for them would be lying about what it knows.

Install

pip install ai-crawler-verify

Python >= 3.8. Zero dependencies, standard library only. No network at import: 2949 prefixes from 12 operator lists ship inside the wheel (78.5 KB), snapshot taken 2026-09-01T14:39:48+00:00.

Command line

ai-crawler-verify 132.196.86.1 --ua "GPTBot/1.2"   # JSON verdict on stdout
ai-crawler-verify 66.249.66.1 --ua "Googlebot/2.1"   # reverse DNS, forward-confirmed
ai-crawler-verify --sources                          # which operator lists are bundled
ai-crawler-verify <ip> --no-dns                      # never touch DNS: fully offline
ai-crawler-verify <ip> --refresh                     # fetch today's prefixes first

Exit status: 0 verified, 1 not verified, 2 no verdict possible, 3 usage. That makes it usable straight from a log pipeline or a fail2ban action.

API

Function Returns
verify(ip, ua=None, allow_dns=True) the verdict dict above; never raises
lookup(ip) every operator prefix list that address falls inside
reverse_dns(ip) {hostname, forward_confirmed, error} — PTR plus the confirming forward lookup
claimed_crawler(ua) what the user-agent claims to be — a claim, never a verdict
sources() / crawlers() the operator lists, and every crawler with how it is verifiable
refresh(url=None) optional, the only network path — today's prefixes, cached 6 h on disk

allow_dns=False guarantees the call makes no DNS query at all, which is what you want inside a request handler; the range check is pure CPU either way.

Why the prefixes are mirrored

Operators publish their ranges at a dozen different URLs in three different shapes, some of them CORS-blocked and some of them slow. The AI Crawler Index re-reads every one of them every six hours and publishes the union in one document — that is what refresh() fetches and what this snapshot was cut from. Every list is also linked back to the operator's own endpoint in sources(), so you can check the mirror against the source.

Limits, stated plainly

  • A True means this address is in the list that operator publishes. If an operator's list is stale or wrong, so is the answer.
  • Reverse DNS is only as good as the hostname suffix the operator documents. Where this package has no documented suffix it reports the resolved hostname and returns None rather than guessing.
  • Prefix lists rotate. The bundled snapshot is a snapshot: for a long-running service, call refresh() (it caches for six hours) or re-install.
  • Data: CC0-1.0. Code: MIT.

Independent, non-commercial, run by automation. Not affiliated with OpenAI, Google, Microsoft, Apple, Perplexity, DuckDuckGo or any other operator named in the table; every prefix list belongs to the operator that publishes it.

Download files

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

Source Distribution

ai_crawler_verify-1.0.0.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

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

ai_crawler_verify-1.0.0-py3-none-any.whl (25.1 kB view details)

Uploaded Python 3

File details

Details for the file ai_crawler_verify-1.0.0.tar.gz.

File metadata

  • Download URL: ai_crawler_verify-1.0.0.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.2

File hashes

Hashes for ai_crawler_verify-1.0.0.tar.gz
Algorithm Hash digest
SHA256 c575e67ae41a6417885f6cf29650d0fbedfc3b0bc5054c32026a56aef95077d6
MD5 c9592d5cf720b24ab2d107fe0ca0da34
BLAKE2b-256 6a5ec2b8e41cc1ef743f906cba06b7f77776a5402d99e9ee0050433aa429350b

See more details on using hashes here.

File details

Details for the file ai_crawler_verify-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ai_crawler_verify-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 451fe400458824613f8eb266e207d7a88b2b7aba0306d7ca7b4eded5dbe59b82
MD5 abf6e10816f0bbab81e869150f820069
BLAKE2b-256 dd61b6d7b279d66a94469ef9ab5ce5a8bff31221362d6f85b418f025b0c5daf6

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.0 This release

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