AI Agent Allowlist
Home Page-Types Database Agent Guardrails API Docs Pricing
Why It Matters
2026 Agent Incidents Category Targeting Database Refreshes Contact Customer Login
Download Free Sample
lookup api · v1 reference

API Documentation

One authenticated GET endpoint. Send any domain or URL; get back the verified page-type map for that domain — the login, checkout, pricing, docs, and contact URLs your agent policy needs to make an allow/deny decision before the request leaves your network. This page is the complete reference: authentication, parameters, response schema, errors, quotas, and runnable examples.

GET https://www.aiagentallowlist.com/api/check?domain={domain}
Quick start

Your first lookup in one line

Subscribe to any plan on the pricing page, copy the API key from your account area, and run this. That is the whole integration surface — there are no SDKs to install and no other endpoints to learn.

curl
curl -H "X-API-Key: YOUR_API_KEY" \
     "https://www.aiagentallowlist.com/api/check?domain=stripe.com"

The response is a single JSON object: whether the domain is in the database, its primary language, the page-type map with one verified URL per confirmed page type, and how many lookups remain on your plan this cycle. Everything below is detail on that one request and its one response.

Authentication

API key, two ways to send it

Every plan includes an API key, issued when your subscription is activated — you will find it in your account area the moment payment completes. Send it with each request either as the X-API-Key header or as an api_key query parameter; both are accepted on every request.

curl · both methods
# Recommended: header authentication
curl -H "X-API-Key: YOUR_API_KEY" \
     "https://www.aiagentallowlist.com/api/check?domain=stripe.com"

# Alternative: query-parameter authentication
curl "https://www.aiagentallowlist.com/api/check?domain=stripe.com&api_key=YOUR_API_KEY"
  Keep the key out of logs: prefer the X-API-Key header in production. Query strings — and any keys embedded in them — routinely end up in proxy logs, web-server access logs, and browser histories. The query-parameter form exists for quick tests and environments where setting headers is awkward.

A request with no key, or with a key that does not match an account, returns 401. A valid key on an account that is not yet activated — or whose monthly quota is exhausted — returns 403 with an explanatory message. Details in the error reference below.

Request

Request parameters

The endpoint is GET https://www.aiagentallowlist.com/api/check over HTTPS. It takes one required parameter and one optional authentication parameter.

ParameterRequiredDescription
domain required The domain to look up. Accepts a bare domain or a full URL — scheme, path, port, and a leading www. are stripped server-side, so domain=stripe.com and domain=https://dashboard.stripe.com/login both resolve to stripe.com. This means you can pass the exact URL your agent is about to open without normalizing it first. A value that cannot be parsed into a domain returns 400.
api_key optional Your API key, if you are not sending it as the X-API-Key header. One of the two must be present on every request.
  Remember to URL-encode. If you pass a full URL as the domain value, percent-encode it (encodeURIComponent, urlencode, or your HTTP client’s params mechanism) so its own query string does not get mixed into the API request’s. All four code samples below do this correctly.
Response

Response schema

Successful lookups return 200 with a JSON object. The shape is identical whether the domain is found or not — only found and the contents of page_types change — so one parser handles both cases.

FieldTypeMeaning
domainstringThe normalized domain the lookup resolved to, after stripping scheme, path, port, and www. Always echo-check this against what you sent.
foundbooleantrue if the domain is in the classified database; false if it is not. For a guardrail, false is itself a signal — see the not-found example below.
languagestringDetected primary language of the site as an ISO 639-1 code (e.g. en). Present on found domains.
page_typesobjectMap of page-type name → verified URL on the domain. Keys are drawn from the 20 page types listed below. Only types that actually exist on the domain appear — if a key is absent, the classification pipeline confirmed there is no such page. Empty object ({}) when found is false.
quota_remainingintegerLookups left on your plan in the current 30-day cycle, after this request. Returned on every metered response, found or not, so you never need a separate usage call.

The 20 page types

page_types keys are drawn from a fixed vocabulary of 20 page types, including:

login checkout pricing docs contact about careers blog product legal sitemap leadership press downloads developer / API …and more

The two highlighted types — login and checkout — are the credential and transaction surfaces most agent policies deny by default. The full page-type definitions are on the database page.

Domain found

Each key maps to the verified live URL — discovered by traversing the site’s actual link structure, not guessed from path patterns.

200 · json
{
  "domain": "0-0-8studios.com",
  "found": true,
  "language": "en",
  "page_types": {
    "login": "http://0-0-8studios.com/m/account",
    "contact": "http://0-0-8studios.com/contact-us",
    "about": "http://0-0-8studios.com/about-0-0-8",
    "careers": "http://0-0-8studios.com/careers",
    "legal": "http://0-0-8studios.com/privacy-policy"
  },
  "quota_remaining": 89999
}

Domain not found

Still 200, still one credit — and for a guardrail, a verdict in its own right: an unclassified domain is a default-deny candidate.

200 · json
{
  "domain": "example-unknown.com",
  "found": false,
  "page_types": {},
  "quota_remaining": 89998
}
  Absence is information. Two distinct negatives matter to policy code: a type missing from page_types on a found domain means the pipeline confirmed the domain has no such page; a domain returning found: false means the classification layer has never seen it. Treat the first as “this surface does not exist here” and the second as “this destination is unvetted” — under a default-deny posture, the second should block or escalate.
Errors

Error reference

Errors use conventional HTTP status codes with a JSON body carrying a human-readable message. Quota-related 403s also include quota fields so your client can distinguish “not activated” from “used up”.

CodeMeaningWhat your client should do
200SuccessParse the JSON. Check found before reading page_types.
400Invalid domain parameterThe value was missing or could not be parsed into a domain. Fix the input; do not retry unchanged.
401Missing or invalid API keyNo key was sent, or the key matches no account. Verify the key against your account area; do not retry until corrected.
403Account not activated, or monthly quota exhaustedRead the JSON message and quota fields to tell the cases apart. Not activated: complete payment/activation. Quota exhausted: wait for the next 30-day cycle or upgrade — there is no automatic overage billing.
429Too many requestsYou hit the per-IP burst guard (roughly 240 requests/minute, independent of your quota). Back off briefly and retry; smooth bursts with a local queue.
503Backend temporarily unavailableTransient. Retry with exponential backoff. In a guardrail, fail closed or serve from your local cache rather than skipping the check.
  Guardrail failure posture: decide up front what your enforcement point does on 429/503. The safe default is to fail closed for unknown domains and fall back to your local cache for domains you have already resolved this cycle — an outage should never widen agent access.
Code samples

The same lookup in four languages

Each sample reads the key from an environment variable, sends it as a header, passes the domain safely encoded, and handles the found / not-found / error cases. Copy, set AAL_API_KEY, run.

curl
# Bare domain
curl -H "X-API-Key: $AAL_API_KEY" \
     "https://www.aiagentallowlist.com/api/check?domain=stripe.com"

# Full URL — let curl handle the encoding with --data-urlencode + -G
curl -G -H "X-API-Key: $AAL_API_KEY" \
     --data-urlencode "domain=https://dashboard.stripe.com/login" \
     "https://www.aiagentallowlist.com/api/check"
python · requests
import os
import requests

API_URL = "https://www.aiagentallowlist.com/api/check"
HEADERS = {"X-API-Key": os.environ["AAL_API_KEY"]}

def check_domain(domain):
    # `domain` may be a bare domain or a full URL — the API normalizes it
    resp = requests.get(API_URL, headers=HEADERS,
                        params={"domain": domain}, timeout=10)
    if resp.status_code == 429 or resp.status_code == 503:
        raise RuntimeError(f"Transient error {resp.status_code} — retry with backoff")
    resp.raise_for_status()
    return resp.json()

record = check_domain("https://dashboard.stripe.com/login")
if record["found"]:
    login_url = record["page_types"].get("login")
    print(f"login page: {login_url}, quota left: {record['quota_remaining']}")
else:
    print("unclassified domain — default-deny candidate")
node · fetch
const API_URL = "https://www.aiagentallowlist.com/api/check";

async function checkDomain(domain) {
  const url = `${API_URL}?domain=${encodeURIComponent(domain)}`;
  const res = await fetch(url, {
    headers: { "X-API-Key": process.env.AAL_API_KEY }
  });
  if (res.status === 429 || res.status === 503) {
    throw new Error(`Transient error ${res.status} — retry with backoff`);
  }
  if (!res.ok) throw new Error(`API error ${res.status}`);
  return res.json();
}

const record = await checkDomain("https://dashboard.stripe.com/login");
if (record.found) {
  console.log("login page:", record.page_types.login ?? "none on this domain");
  console.log("quota left:", record.quota_remaining);
} else {
  console.log("unclassified domain — default-deny candidate");
}
php · curl
<?php
function check_domain($domain) {
    $url = 'https://www.aiagentallowlist.com/api/check?domain=' . urlencode($domain);
    $ch  = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_HTTPHEADER     => ['X-API-Key: ' . getenv('AAL_API_KEY')],
    ]);
    $body   = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($status === 429 || $status === 503) {
        throw new RuntimeException("Transient error $status — retry with backoff");
    }
    if ($status !== 200) {
        throw new RuntimeException("API error $status: $body");
    }
    return json_decode($body, true);
}

$record = check_domain('https://dashboard.stripe.com/login');
if ($record['found']) {
    echo 'login page: ', $record['page_types']['login'] ?? 'none on this domain', PHP_EOL;
    echo 'quota left: ', $record['quota_remaining'], PHP_EOL;
} else {
    echo 'unclassified domain — default-deny candidate', PHP_EOL;
}
Integration pattern

Using the API in an egress guardrail

The canonical deployment: your agent framework, gateway, or proxy intercepts every URL an agent wants to open, resolves the domain against the API, and applies page-type policy before the request is sent. Cache each domain’s record locally — agents revisit the same domains constantly, and one cached lookup covers every URL on that domain.

pseudo-code · egress hook
# Policy: which page types this fleet may touch
ALLOW_TYPES = {"pricing", "docs", "blog", "about", "contact"}
DENY_TYPES  = {"login", "checkout"}

def evaluate_navigation(url):
    domain = extract_domain(url)          # or just pass the full URL as `domain`

    record = cache.get(domain)            # local cache first — one lookup per domain
    if record is None:
        record = api_check(domain)        # GET /api/check?domain=...
        cache.set(domain, record, ttl=CACHE_TTL)

    if not record["found"]:
        return DENY("unclassified domain")   # default-deny: unknown = unvetted

    page_type = match_url_to_type(url, record["page_types"])
    if page_type in DENY_TYPES:
        return DENY(f"denied page type: {page_type}")   # log + alert
    if page_type in ALLOW_TYPES:
        return ALLOW(page_type)
    return FLAG(url)                      # unmatched URL on a known domain → review

Three practices make this pattern hold up in production. Cache aggressively: keying the cache by domain means a fleet that opens ten thousand URLs a day on five hundred domains spends five hundred lookups, not ten thousand. Log every verdict — allow, deny, and flag alike — with the page type and URL, so denials feed alerts and allows feed policy reviews. Keep the default-deny branch honest: the found: false path is where the 2026-style incidents get stopped, so route it to denial or human review, never to silent allow. Broader policy design — fleet scoping, chokepoints, audit trails — is covered in the enterprise guardrails guide and on the agent guardrails page.

Quotas & plans

Metering, rate limits, and plan quotas

Each successfully served lookup — found or not — consumes one credit. Quotas run on 30-day cycles from your subscription date, and every metered response carries quota_remaining. Separately from the quota, a coarse per-IP burst guard of roughly 240 requests per minute smooths traffic spikes; hitting it returns 429 and does not consume credits.

PlanPrice / monthLookups / month 
Pro$9990,000Subscribe →
Pro Plus$249225,000Subscribe →
Advanced$499450,000Subscribe →
Advanced Plus$999900,000Subscribe →
Business$1,9972,750,000Subscribe →
Enterprise$3,99910,000,000Subscribe →

Exhausted quotas return 403 until the next cycle — there is no automatic overage billing and your card is never charged beyond the plan price. Need more than 10M lookups a month, or want the data on-prem with no metering at all? See database licenses and OEM options on the pricing page.

FAQ

API questions, answered

What does one lookup return?
One GET request to /api/check with a domain parameter returns whether the domain is in the database, its detected primary language, and a page_types object mapping each confirmed page type — login, checkout, pricing, docs, contact, about, careers, blog, legal, and the rest of the 20-type vocabulary — to its verified URL on that domain. Every metered response also includes quota_remaining.
How do I authenticate?
Send your API key either as an X-API-Key header (recommended for production — it keeps the key out of URL logs) or as an api_key query parameter. Keys are issued on subscription and shown in your account area the moment your plan is activated.
What does found: false mean for a guardrail?
The domain is not among the 40M+ classified domains. For an egress guardrail that is itself a policy signal: a destination the classification layer has never seen is unvetted, and under a default-deny posture it should be denied or routed to human review rather than silently allowed. The not-found response still returns 200 and consumes one credit.
A page type is missing from page_types — did the check fail?
No. Only page types that actually exist on the domain appear in the map. An absent key on a found: true response means the classification pipeline confirmed the domain has no such page — a verified negative your policy can rely on, not a coverage gap.
Can I pass a full URL instead of a bare domain?
Yes. Scheme, path, port, and www are stripped server-side, so domain=stripe.com and domain=https://dashboard.stripe.com/login both resolve to stripe.com. Just make sure the value is URL-encoded so the passed URL’s own query string is not mixed into the API request’s.
How are lookups counted, and what happens at the quota?
Each successfully served lookup consumes one credit, found or not. Quotas run on 30-day cycles from your subscription date — 90,000/month on the $99 Pro plan up to 10,000,000 on the $3,999 Enterprise plan. Beyond the quota, requests return 403 with a message and quota fields until the next cycle; there is no overage billing. Upgrading applies the larger quota immediately.
Is there a rate limit separate from the quota?
Yes — a coarse per-IP burst guard of roughly 240 requests per minute, independent of your monthly quota. Hitting it returns 429; back off briefly and retry. A local queue plus a per-domain cache keeps almost every real deployment well under it.
Should I cache responses?
Yes, and by domain. Page-type maps change on the timescale of site redesigns, not requests, so caching a domain’s record locally is both allowed and recommended — it cuts your credit usage dramatically and keeps your guardrail’s decision latency independent of the network. On 429/503 errors, serving from the cache while failing closed for unknown domains is the recommended posture.

Get a key and make your first lookup today

Plans start at $99/month for 90,000 lookups. Subscribe, pay by PayPal or card, and your key is active the moment payment completes.

View Plans & Subscribe