Node.js and Python clients for the AI Tools Blocklist API, the AI tool classification database behind AI acceptable-use policies on corporate and school networks. This repository holds both clients, the API reference they implement, the feed formats the same data ships in, and the policy profiles that turn the list into a deployable ruleset.
| Node.js | Python | |
|---|---|---|
| Package | aiblocklist on npm |
aiblocklist on PyPI |
| Install | npm install aiblocklist |
pip install aiblocklist |
| Source | node/ |
python/ |
| Runtime | Node.js 14+, zero dependencies | Python 3.7+, requests only |
One lookup classifies one domain. A found record carries the classification and the vendor's data-use position:
{
"domain": "chatgpt.com",
"blocked": true,
"primary_category": "Text & Language",
"ai_type": "ai_native",
"categories": [{"category": "Text & Language", "subcategory": "General assistants & chatbots"}],
"trains_on_data": "opt_out_default",
"opt_out_available": "yes",
"enterprise_no_training": "yes",
"api_no_training": "yes",
"terms_checked": "2026-09-17",
"quota_remaining": 9999986
}A domain that is not an AI tool returns {"domain": "example.com", "blocked": false, "categories": [], "quota_remaining": ...}. Both are HTTP 200.
The list has 20,000+ AI-tool domains in 18 functional categories with subcategories, rebuilt daily, extracted from a 120-million-domain web filtering infrastructure. The public statistics endpoint shows the live totals per category without a key.
Node.js:
const AIBlocklistClient = require('aiblocklist');
const client = new AIBlocklistClient(process.env.ATB_API_KEY);
const r = await client.check('midjourney.com');
console.log(r.isBlocked, r.primaryCategory, r.subcategoryNames, r.trains_on_data);Python:
from aiblocklist import AIBlocklistClient
client = AIBlocklistClient("YOUR_API_KEY")
r = client.check("midjourney.com")
print(r.blocked, r.primary_category, r.subcategory_names, r.trains_on_data)Command line, Python:
ATB_API_KEY=... python -m aiblocklist chatgpt.com notion.so example.comBase URL: https://www.aitoolsblocklist.com. Authenticated calls send the key as the X-API-Key header (api_key= as a query parameter is also accepted). Pass a bare domain; both clients strip scheme, path, port and a leading www. before sending, and subdomains resolve to the registrable domain on the server.
| Endpoint | GET /api/check?domain=<domain> |
| Key | required, one lookup charged per call |
| Node | check(domain), isBlocked(domain), checkMany(domains, pauseMs), dataUse(domain) |
| Python | check(domain), is_blocked(domain), check_many(domains, pause), data_use(domain) |
| Errors | 400 missing domain, 401 unknown key, 403 quota exhausted or inactive account, 503 gateway busy (retried) |
Fields on a found record:
| Field | Values |
|---|---|
blocked |
true |
primary_category |
one of the 18 categories |
ai_type |
ai_native (the product is the AI) or ai_enabled (a product with an AI feature) |
categories |
every {category, subcategory} pair the tool belongs to |
trains_on_data |
yes, no, opt_out_default, unstated |
opt_out_available |
yes, no, unstated |
enterprise_no_training |
yes, no, unstated |
api_no_training |
yes, no, unstated |
terms_checked |
date the vendor terms were last read |
quota_remaining |
lookups left in the current 30-day cycle |
Feed and database plans download the full classified list.
| Action | Returns | Node | Python |
|---|---|---|---|
GET /api/database/?action=status |
plan, key state, subscribed file, last update | feeds.status() |
feeds.status() |
GET /api/database/?action=database_info |
file name, timestamp, size | feeds.databaseInfo() |
feeds.database_info() |
GET /api/database/?action=download_database |
CSV stream | feeds.downloadDatabase(path) |
feeds.download_database(path) |
GET /api/database/?action=download_categories |
CSV stream of the category tree | feeds.downloadCategories(path) |
feeds.download_categories(path) |
A lookup-only plan receives HTTP 403 with the plan named in the body; both clients raise PlanError for it.
| Endpoint | Node | Python |
|---|---|---|
GET /api/stats.php |
stats(), categories() |
stats(), categories() |
GET /api/data-use-clause.php?d=&f= |
clause(domain, field) |
clause(domain, field) |
clause() returns {clause, url}: the verbatim sentence from the vendor's terms and the page it came from. Fields: trains_consumer_default, optout_available, enterprise_no_training, api_no_training.
The same list ships in the formats network equipment already consumes, from the account area on feed plans:
| Format | Consumer |
|---|---|
| EDL (plain domain list) | firewall External Dynamic Lists |
| PAC | browser proxy auto-config, block or route by host |
| hosts | resolvers and endpoints that load a hosts file |
| DNS feed | DNS filtering services and sinkholes |
| CSV and JSON | the full classified list with categories, AI types and data-use fields |
Feeds can be filtered by category before loading, and by policy profile (below).
Paid plans include 15 sector profiles that turn the raw classification into a verdict per domain: Block, Controls or Allow. Profiles exist for generic enterprise, MSP, K-12, higher education, healthcare, financial services, insurance, legal, government, defense, accounting, manufacturing, software, agencies and pharma. Each profile's feed is available in the same EDL, PAC, hosts and DNS formats, in a standard and a strict mode.
The verdict is what most deployments want to load, because it already encodes the category defaults, the subcategory overrides and the training-terms tightening for that sector. The clients in this repository return the underlying classification; the profile feeds are served from the account area.
A gateway or proxy plugin caches the verdict per host for a day and enforces per category. This one blocks generative media, flags assistants that train on input by default, and lets the rest through with a header the DLP layer can key on.
const AIBlocklistClient = require('aiblocklist');
const client = new AIBlocklistClient(process.env.ATB_API_KEY);
const cache = new Map();
const BLOCK = new Set(['Image & Visual', 'Audio & Voice', 'Companions & Social']);
async function decide(host) {
const hit = cache.get(host);
if (hit && hit.expires > Date.now()) return hit.verdict;
const r = await client.check(host);
let verdict = 'allow';
if (r.isBlocked) {
if (r.categoryNames.some((c) => BLOCK.has(c))) verdict = 'block';
else if (['yes', 'opt_out_default'].includes(r.trains_on_data)) verdict = 'warn';
}
cache.set(host, { verdict, expires: Date.now() + 86400000 });
return verdict;
}import csv, os
from aiblocklist import AIBlocklistClient
client = AIBlocklistClient(os.environ["ATB_API_KEY"])
client.feeds.download_database("/var/lib/aiblocklist/ai_tools_full.csv")
BLOCK = {"Image & Visual", "Audio & Voice", "Companions & Social"}
with open("/var/lib/aiblocklist/ai_tools_full.csv", encoding="utf-8") as src, \
open("/etc/dnsmasq.d/ai-sinkhole.conf", "w") as out:
for row in csv.DictReader(src):
if row["primary_category"] in BLOCK:
out.write("address=/%s/0.0.0.0\n" % row["domain"])Run nightly after the daily rebuild, then reload the resolver.
from aiblocklist import AIBlocklistClient
client = AIBlocklistClient("YOUR_API_KEY")
def enrich(alert):
r = client.check(alert["dest_host"])
if not r.blocked:
return dict(alert, ai_tool=False)
clause = client.clause(r.domain, "trains_consumer_default")
return dict(alert, ai_tool=True, ai_category=r.primary_category,
ai_subcategories=r.subcategory_names, vendor_trains=r.trains_on_data,
vendor_terms_checked=r.terms_checked,
vendor_clause=clause and clause["clause"], vendor_clause_url=clause and clause["url"])Both clients expose the same surface with the naming conventions of their language: checkMany and check_many, feeds.databaseInfo() and feeds.database_info(). The Node client has no dependencies and works on Node.js 14 and newer, which keeps it usable inside gateway plugins and serverless functions that ship no node_modules. The Python client depends on requests only and runs on Python 3.7 and newer, so it fits the interpreters found on network appliances and older management hosts.
Lookups are metered per call and reset every 30 days; quota_remaining on each response tells you where you stand. Cache verdicts for a day in your own process or datastore, because the list is rebuilt daily and nothing changes faster than that. The Lookup API plan covers the lookup endpoints; Blocklist Feeds and Professional Annual plans add the database download, the hosted feeds and the policy profiles. On a lookup-only plan the database methods raise PlanError with the plan named in the response body, so a script can detect the situation and fall back to lookups rather than fail silently.
Keys should come from an environment variable or a secrets manager rather than source control. Both clients send the key as a header, never in the URL, so it stays out of proxy and access logs. Downloads stream to disk in chunks, so the full database file is never held in memory.
Every use of an AI tool is a request to a hostname. That is the one fact every control point on a network can see without an agent, without TLS inspection and without user cooperation. Classifying the hostname is the step that turns an acceptable-use policy into an enforceable rule at the resolver, the proxy or the firewall.
Generic filtering categories stop at "AI". Real policies do not: a school allows a tutor and blocks a face-swap tool, a bank allows a coding assistant on an enterprise tier that does not train and blocks a consumer chatbot that does. Those rules need a category, a subcategory, an AI type and the vendor's training terms, which is exactly what one record here carries.
The frameworks organisations are measured against ask for the same thing. The NIST AI Risk Management Framework puts an inventory of AI systems and third-party data flows at the front of its Map function. The OWASP Top 10 for LLM Applications lists sensitive information disclosure among its leading risks, and for organisations whose staff use hundreds of external LLM products the mitigation lives at the network edge. MITRE ATLAS catalogues how AI systems are targeted and misused, which is a reason to know which ones are in use. Under the EU AI Act, deployers carry obligations that presume they know what is deployed.
Most organisations start by finding out what is already in use. The shadow AI discovery tool takes a DNS, proxy or firewall export (Cisco Umbrella, Zscaler, Palo Alto, Fortinet, Cloudflare Gateway, DNSFilter, NextDNS, Pi-hole, SonicWall, Squid, or any CSV or hostname list) and returns every AI tool reached from the network, with category, risk flags, the vendor training verdict, a per-user breakdown, a sanctioned versus unsanctioned split, and a CSV and PDF evidence pack. It matches against the same list these clients query, so what an audit finds is what check() classifies.
The reverse direction, what an organisation's own browsing agents may open on the web, is a separate product: the AI agent allow list. It holds verified page-type URLs (login, signup, checkout, upload, settings, pricing, documentation and more, up to 28 per domain) across 40 million+ domains and returns a per-URL allow or deny verdict so an agent can read freely while credential and payment surfaces stay off-limits. Together, agent guardrails data for agents and a classified blocklist for people cover both directions of AI governance.
What is an AI blocklist? A list of domains belonging to AI tools, classified so a filter can block, allow or flag them by type rather than as one flat "AI" category. This one holds 20,000+ domains in 18 categories with subcategories, refreshed daily, with the vendor's data-use terms attached.
Is there a difference between aiblocklist and aitoolsblocklist?
Both talk to the same API. aiblocklist is positioned for enforcement points: a feeds sub-client for the database downloads, a Lookup object with accessors, a command line entry. aitoolsblocklist is the original client with the flatter method surface. Use whichever fits your code; the records are identical.
How do I block AI tools on a company network?
Load a feed (EDL, PAC, hosts or DNS) into the filter you already run, or call check() from a proxy hook and enforce per category, or download the database nightly and match locally. All three are shown above.
Can I allow some AI tools and block others? Yes. Every record has a primary category, a multi-label category list, an AI type and the training terms. Paid plans add 15 policy profiles with ready-made Block, Controls and Allow verdicts per sector.
Does the data say whether a vendor trains on my input?
Yes: trains_on_data, opt_out_available, enterprise_no_training, api_no_training and terms_checked on every record, and clause() returns the verbatim clause with its URL.
Is there a bulk lookup?
Lookups are one domain per call. checkMany() and check_many() run them sequentially and deduplicate first. For thousands of hosts, download the database and match locally.
How often is the list refreshed?
Daily. stats() shows the live totals without a key.
Who builds it? Alpha Quantum, also behind the website categorization API, the web filtering database, the phishing detection API, the school web filtering database and the PII detection API.
| Package | npm | PyPI | Product |
|---|---|---|---|
| aitoolsblocklist | npm | PyPI | AI tools blocklist |
| shadowaitools | npm | PyPI | shadowaitools.com |
| aiagentallowlist | npm | PyPI | aiagentallowlist.com |
| phishingdetectionapi | npm | PyPI | phishingdetectionapi.com |
| webfilteringdatabase | npm | webfilteringdatabase.com | |
| websitecategorization | npm | PyPI | websitecategorizationapi.com |
| cipawebfiltering | npm | PyPI | cipawebfiltering.com |
Mirror: gitlab.com/url-classifications/aiblocklist.
- Product, plans and feeds: https://www.aitoolsblocklist.com
- NIST AI Risk Management Framework: https://www.nist.gov/itl/ai-risk-management-framework
- OWASP Top 10 for LLM Applications: https://owasp.org/www-project-top-10-for-large-language-model-applications/
- MITRE ATLAS: https://atlas.mitre.org/
- EU AI Act: https://eur-lex.europa.eu/eli/reg/2024/1689/oj
MIT, Alpha Quantum. Contact: info@alpha-quantum.com