Query 16,024+ classified AI domains programmatically. Single lookups under 50ms, bulk queries up to 1,000 per request, and daily delta sync — all via authenticated JSON endpoints.
The REST API provides programmatic access to every AI-tool domain identified through our 102-million-domain classification pipeline. Each response returns structured JSON with rich classification data.
Primary + subcategory from 18-category taxonomy
0.0–1.0 classification confidence score
Low, medium, or high risk classification
Cloud-processed, on-device, or hybrid posture
First-seen and last-verified dates per domain
Active or inactive domain tracking
The base URL for all requests is https://api.aitoolsblocklist.com, versioned under /v1/. When v2 ships, v1 continues for at least 12 months.
Content-Type: application/jsonAccept-Encoding: gzipThe API is designed for machine-to-machine integration — firewall scripts, SOAR playbooks, SIEM pipelines, and security dashboards. For a pre-built file your firewall polls directly, see our External Dynamic List (EDL) guide.
Every request requires a valid API key in the Authorization header using the Bearer scheme. Keys are generated in your account dashboard after subscribing to any paid plan.
atb_ prefix for easy log identificationKeys are scoped to your subscription plan — upgrading automatically upgrades all active keys. Revoked keys return 401 within 60 seconds.
# Authentication: pass your API key as a Bearer token curl -H "Authorization: Bearer atb_your_api_key_here" \ "https://api.aitoolsblocklist.com/v1/stats" # Missing or invalid key returns 401: { "error": "unauthorized", "message": "Invalid or missing API key. Include a valid key in the Authorization header.", "status": 401 } # Expired or revoked key returns 403: { "error": "forbidden", "message": "API key has been revoked. Generate a new key in your dashboard.", "status": 403 }
GET /v1/lookup/{domain}Check whether a domain is in the AI Tools Blocklist and get its full classification. Ideal for real-time workflows — SOAR playbooks, proxy plugins, and custom DNS resolvers.
Pass the bare domain name without protocol or path — e.g. claude.ai, not https://claude.ai/chat. Looking up chat.openai.com returns the classification for openai.com.
# Lookup a single domain curl -H "Authorization: Bearer atb_your_api_key_here" \ "https://api.aitoolsblocklist.com/v1/lookup/claude.ai" # Response (200 OK) — domain is in the blocklist: { "domain": "claude.ai", "in_blocklist": true, "primary_category": "Text & Language", "subcategory": "General assistants & chatbots", "confidence": 0.99, "risk_tier": "high", "data_handling": "cloud-processed", "status": "active", "first_seen": "2023-07-11T00:00:00Z", "last_verified": "2026-07-09T06:00:00Z" } # Response (200 OK) — domain is NOT in the blocklist: { "domain": "example.com", "in_blocklist": false }
The in_blocklist boolean tells you whether the domain is classified as an AI tool. Your integration checks one field — no ambiguity between "not found" and "invalid request."
GET /v1/domainsReturns the complete classified domain list with optional filters for category, confidence, risk tier, and status. Use this for initial data loads into a local cache, DNS sinkhole, or firewall EDL.
# Full list — first page, default filters curl -H "Authorization: Bearer atb_your_api_key_here" \ "https://api.aitoolsblocklist.com/v1/domains" # With filters — only high-confidence Text & Language domains curl -H "Authorization: Bearer atb_your_api_key_here" \ "https://api.aitoolsblocklist.com/v1/domains?category=Text+%26+Language&min_confidence=0.9&status=active" # Response: { "domains": [ { "domain": "openai.com", "primary_category": "Text & Language", "subcategory": "General assistants & chatbots", "confidence": 0.99, "risk_tier": "high", "status": "active" }, // ... up to 5,000 records per page ], "pagination": { "cursor": "eyJpZCI6NTAwMH0=", "has_more": true, "total": 16024 } }
Pass the cursor value as a query parameter to get the next page. Cursors expire after 24 hours — adjust page size with limit (min 100, max 10,000).
GET /v1/deltaReturns only domains added, removed, or reclassified since a given timestamp. This is the backbone of any automated sync workflow after your initial full load.
since parameterPOST /v1/bulk-lookupClassify up to 1,000 domains in a single request. Ideal for processing firewall logs, DNS query logs, or proxy access logs in batch.
# Bulk lookup — classify multiple domains in one request curl -X POST \ -H "Authorization: Bearer atb_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "domains": [ "openai.com", "claude.ai", "example.com", "midjourney.com", "jasper.ai" ] }' \ "https://api.aitoolsblocklist.com/v1/bulk-lookup" # Response: { "results": [ { "domain": "openai.com", "in_blocklist": true, "primary_category": "Text & Language", "confidence": 0.99 }, { "domain": "claude.ai", "in_blocklist": true, "primary_category": "Text & Language", "confidence": 0.99 }, { "domain": "example.com", "in_blocklist": false }, { "domain": "midjourney.com", "in_blocklist": true, "primary_category": "Image & Visual", "confidence": 0.99 }, { "domain": "jasper.ai", "in_blocklist": true, "primary_category": "Text & Language", "confidence": 0.97 } ], "queried": 5, "matched": 4 }
GET /v1/taxonomyReturns the complete category tree with stable IDs, human-readable names, and subcategories. Cache aggressively — updates happen at most once per quarter.
GET /v1/statsReturns aggregate database stats: total domains, category breakdown, last update timestamp, 24-hour additions, and 7-day trends. Use it for dashboard widgets and health checks.
# Database statistics curl -H "Authorization: Bearer atb_your_api_key_here" \ "https://api.aitoolsblocklist.com/v1/stats" # Response: { "total_domains": 16024, "active_domains": 15684, "categories": 18, "subcategories": 182, "last_updated": "2026-07-09T06:00:00Z", "added_last_24h": 87, "removed_last_24h": 12, "top_categories": [ { "name": "Text & Language", "count": 9842 }, { "name": "Image & Visual", "count": 7215 }, { "name": "Code & Development", "count": 5631 } ] }
Rate limits are enforced per API key and vary by plan. Exceeding your limit returns a 429 Too Many Requests response with retry headers.
requests / hour
10 bulk lookups/hr
2 full downloads/day
requests / hour
100 bulk lookups/hr
24 full downloads/day
requests / hour
Unlimited bulk lookups
Unlimited downloads
Every response includes IETF-standard rate-limit headers so your client can track its budget without a local counter.
# Rate limit headers included in every response HTTP/1.1 200 OK X-RateLimit-Limit: 5000 # Your hourly limit X-RateLimit-Remaining: 4872 # Requests remaining this window X-RateLimit-Reset: 1752055200 # Unix timestamp when window resets Retry-After: 0 # Seconds to wait (0 = you're fine) # When rate limited (429): HTTP/1.1 429 Too Many Requests X-RateLimit-Limit: 500 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1752055200 Retry-After: 342 # Wait 342 seconds before retrying { "error": "rate_limit_exceeded", "message": "Rate limit exceeded. Retry after 342 seconds.", "status": 429, "retry_after": 342 }
Read the Retry-After header value from the 429 response.
Wait the specified number of seconds before retrying.
If the retry also returns 429, double the wait time.
Cap the maximum backoff at 5 minutes. Tight retry loops can trigger temporary key suspension.
Complete, copy-pasteable implementations for the three most common patterns. Each includes authentication, error handling, and rate-limit awareness.
Fetches delta updates, applies changes to a local domain set, and persists state. Drop into a SOAR playbook, Airflow DAG, or standalone cron script.
import requests import json import time from datetime import datetime, timedelta API_KEY = "atb_your_api_key_here" BASE_URL = "https://api.aitoolsblocklist.com/v1" HEADERS = {"Authorization": f"Bearer {API_KEY}"} def sync_blocklist(last_sync_file="last_sync.json"): # Load last sync state try: with open(last_sync_file) as f: state = json.load(f) domains = set(state["domains"]) since = state["last_sync"] except FileNotFoundError: # First run — do a full load domains = set() since = None if since is None: # Full load with pagination cursor = None while True: params = {"limit": 10000} if cursor: params["cursor"] = cursor resp = requests.get(f"{BASE_URL}/domains", headers=HEADERS, params=params) resp.raise_for_status() data = resp.json() for rec in data["domains"]: domains.add(rec["domain"]) if not data["pagination"]["has_more"]: break cursor = data["pagination"]["cursor"] print(f"Full load complete: {len(domains)} domains") else: # Delta sync resp = requests.get(f"{BASE_URL}/delta", headers=HEADERS, params={"since": since}) if resp.status_code == 429: retry = int(resp.headers.get("Retry-After", 60)) print(f"Rate limited. Retrying in {retry}s.") time.sleep(retry) return sync_blocklist(last_sync_file) resp.raise_for_status() delta = resp.json() for rec in delta.get("added", []): domains.add(rec["domain"]) for rec in delta.get("removed", []): domains.discard(rec["domain"]) print(f"Delta sync: +{len(delta.get('added',[]))} " f"-{len(delta.get('removed',[]))} = {len(domains)} total") # Persist state with open(last_sync_file, "w") as f: json.dump({ "domains": sorted(domains), "last_sync": datetime.utcnow().isoformat() + "Z" }, f) return domains
Checks a domain in real time for proxy middleware, extension backends, or serverless enrichment functions. Returns the classification object or null.
const API_KEY = 'atb_your_api_key_here'; const BASE = 'https://api.aitoolsblocklist.com/v1'; async function checkDomain(domain) { const res = await fetch( `${BASE}/lookup/${encodeURIComponent(domain)}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } } ); if (res.status === 429) { const retry = parseInt(res.headers.get('Retry-After') || '60', 10); await new Promise(r => setTimeout(r, retry * 1000)); return checkDomain(domain); } if (!res.ok) throw new Error(`API error: ${res.status}`); const data = await res.json(); return data.in_blocklist ? data : null; } // Usage const result = await checkDomain('claude.ai'); if (result) { console.log(`Blocked: ${result.domain} — ${result.primary_category}`); } else { console.log('Domain is not an AI tool.'); } // Bulk check from Node.js async function bulkCheck(domains) { const res = await fetch(`${BASE}/bulk-lookup`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ domains }) }); const data = await res.json(); return data.results.filter(r => r.in_blocklist); }
Downloads the full domain list in plain-text format for firewall or DNS resolver consumption. Validates the download before overwriting the live file.
#!/bin/bash # Daily AI blocklist sync — add to crontab: # 0 3 * * * /opt/scripts/sync-ai-blocklist.sh API_KEY="atb_your_api_key_here" FEED_URL="https://api.aitoolsblocklist.com/v1/domains?format=plain&status=active" LIVE_FILE="/etc/blocklists/ai-tools-domains.txt" TEMP_FILE="/tmp/ai-blocklist-download.txt" # Download to temp file curl -sf -H "Authorization: Bearer $API_KEY" \ -o "$TEMP_FILE" "$FEED_URL" if [ $? -ne 0 ]; then echo "[ERROR] Download failed. Keeping existing list." exit 1 fi # Validate: must have at least 10,000 lines LINE_COUNT=$(wc -l < "$TEMP_FILE") if [ "$LINE_COUNT" -lt 10000 ]; then echo "[ERROR] Only $LINE_COUNT lines — expected 10,000+. Aborting." exit 1 fi # Atomic swap and reload mv "$TEMP_FILE" "$LIVE_FILE" echo "[OK] Updated AI blocklist: $LINE_COUNT domains." # Reload your DNS resolver or firewall # systemctl reload unbound # pfctl -t ai_block -T replace -f /etc/blocklists/ai-tools-domains.txt
Every error response includes a JSON body with error, message, and status fields. Handle each code explicitly — a 429 needs backoff, a 400 needs a request fix.
| 200 | Request succeeded. Response body contains data. |
| 204 | Success, no content (used for key revocation confirmation). |
| 400 | Bad request — invalid parameter, malformed JSON, or missing required field. |
| 401 | Unauthorized — missing or invalid API key. |
| 403 | Forbidden — key revoked, plan expired, or endpoint not included in your plan. |
| 429 | Rate limit exceeded — wait and retry per Retry-After header. |
| 500 | Server error — retry after 30 seconds. If persistent, contact support. |
If some domains in a bulk request fail validation (e.g. IP addresses or unsupported TLDs), those entries return with an error field while valid domains are classified normally. The HTTP status code is still 200 — check individual result entries for per-domain errors.
Three patterns for different security stacks and operational cadences.
Load the full list once into Redis, SQLite, or a flat file. Run a daily cron job with the delta endpoint to stay current. Supports millions of local lookups from a 500 req/hr plan.
Call the single-domain endpoint from SOAR playbooks, SIEM enrichment, or proxy middleware. Under 50ms latency. Requires Business or Enterprise plan for higher rate limits.
Extract unique domains from logs, classify via bulk lookup in batches of 1,000. Feed results into your SIEM for dashboards and alerting. Run hourly, daily, or on-demand.
The /v1/domains endpoint uses opaque cursors rather than page offsets. Cursors are stable across dataset changes — no duplicates or gaps within a pagination run.
limit param (100–10,000)Changing a filter parameter invalidates existing cursors — restart from the first page with the new filter.
Operational practices from production integrations across hundreds of deployments — firewall teams, SOC analysts, and security vendors consuming the API at scale.
Load the full list into a local cache (Redis, file, or in-memory set) and resolve lookups locally. Refresh via delta once per day — supports millions of lookups from a 500 req/hr plan.
Never overwrite your live blocklist without checking line count and format first. A truncated download can block nothing or block everything.
Generate distinct keys for production, staging, and dev. If a dev key leaks, revoke it instantly without interrupting production sync.
Add a health check that verifies last_updated is within 48 hours. Alert if stale — it means either our pipeline or your sync script has an issue.
Request an API key to start querying 16,024+ classified AI domains. All plans include full API access — the difference is rate limits and refresh frequency.
Tell us about your integration — platform, expected query volume, and preferred plan. We will provision API credentials within 24 hours.