Command Palette

Search for a command to run...

HTTP endpoint

Call the AI Site Scanner from your own code with GET or POST /api/ai-scan: parameters, the JSON response and ScanResult type, error codes and rate limits.

The scanner page, the command line and the MCP server all call one endpoint, https://toolz.dev/api/ai-scan, and you can call it too. Send a URL with GET or POST, and it returns { "success": true, "data": <ScanResult>, "reportId": "<id>" }: the full scan result as JSON and the id of the stored report.

Unversioned. This endpoint has no version number and no API key. It is the scanner's own endpoint, documented so you can use it, and its parameters and response may change until a versioned API ships. If you build on it, read fields defensively and expect to adjust your code when it changes. For a CI check, the command line already handles the response and the exit codes for you.

How do I call it?

With GET and query parameters:

bash
curl "https://toolz.dev/api/ai-scan?url=example.com"
curl "https://toolz.dev/api/ai-scan?url=example.com&mode=site&siteType=content"

Or with POST and a JSON body:

bash
curl -X POST https://toolz.dev/api/ai-scan \
  -H "Content-Type: application/json" \
  -d '{"url": "example.com", "mode": "site", "siteType": "content", "checks": ["llms-txt", "json-ld"]}'

Both run the same scan. Each request fetches the target site live; results are never cached.

What parameters does it take?

Parameter GET POST body Values Default
url Query String A domain or page URL. https:// is added when there is no scheme. At most 2000 characters Required
mode Query String site grades up to 5 pages from the sitemap as well. Anything else is a page scan Page scan
siteType Query String content or api. Anything else means all checks all
checks Query, repeated (checks=a&checks=b) or comma-separated (checks=a,b) Array of strings Check ids from What the scan checks. Unknown ids are dropped. If none are left, every check runs Every check for the siteType
visibility Query String public or unlisted. unlisted is a private scan and needs a Toolz Pro sign-in token public

checks narrows the checks chosen by siteType; it does not add checks from another type. For a private scan, send the Pro account's token as Authorization: Bearer <token>. Public and private scans, limits and Pro explains how that is checked.

What does a successful response look like?

HTTP 200 with:

json
{
  "success": true,
  "data": {
    "url": "https://example.com/",
    "domain": "example.com",
    "scannedAt": "2026-09-23T10:00:00.000Z",
    "fetchOk": true,
    "ttfbMs": 212,
    "platform": { "name": "Next.js", "confidence": 0.9, "evidence": ["..."] },
    "overallScore": 84,
    "grade": "B",
    "categories": [
      { "key": "content", "label": "Content Accessibility", "score": 69, "weight": 48, "passed": ["..."], "issues": ["..."] }
    ],
    "topIssues": ["..."],
    "checks": ["..."],
    "siteType": "content",
    "mode": "page"
  },
  "reportId": "a1B2c3D4e5F6"
}

The values above are illustrative. reportId is null when the result could not be stored; the scan result is complete either way. The report page for an id is https://toolz.dev/ai-scan/r/<reportId>.

A site that could not be loaded still returns HTTP 200 with success: true, but with fetchOk: false, a fetchError message, a score of 0, grade F, empty arrays and reportId: null. Check data.fetchOk before reading the score. Troubleshooting lists the fetchError messages.

What is in ScanResult?

These are the TypeScript types the endpoint returns, from the scanner's source:

ScanResult
interface ScanResult {
  url: string              // final URL after redirects
  domain: string
  scannedAt: string        // ISO 8601
  fetchOk: boolean
  fetchError?: string      // only when fetchOk is false
  ttfbMs: number
  platform: Platform
  overallScore: number     // 0-100
  grade: string            // "A+" .. "F"
  categories: CategoryResult[]
  topIssues: Issue[]       // up to 6, most severe first
  checks: CheckResult[]    // every check that ran
  siteType: 'content' | 'api' | 'all'
  mode?: 'page' | 'site'
  pages?: PageResult[]     // whole-site scans only, worst first
}

interface CheckResult {
  id: string               // e.g. "llms-txt"
  category: 'discoverability' | 'content' | 'botAccess' | 'protocol' | 'commerce'
  label: string
  description: string
  weight: number
  status: 'pass' | 'fail' | 'na'
  detail: string           // what was found
  fixPrompt: string
  docs?: string            // reference URL
}

interface CategoryResult {
  key: string
  label: string
  score: number            // 0-100
  weight: number           // total weight of the evaluated checks
  passed: string[]         // labels of passing checks
  issues: Issue[]          // failing checks
}

interface Issue {
  id: string
  title: string
  severity: 'critical' | 'warning' | 'info'
  detail: string
  fixPrompt: string
  docs?: string
}

interface Platform {
  name: string
  confidence: number       // 0-1
  evidence: string[]
}

interface PageResult {
  url: string
  score: number
  grade: string
  failed: number           // failing checks on that page
}

checks is the complete list and the one to use in code: every check that ran, passed or failed, with its fix prompt. topIssues is the short list the scanner page shows. Scores, grades and scan filters explains how score, weight and severity are derived.

How do I authenticate?

The endpoint is free and needs no key for a single public page. The paid modes do need one: mode=site grades up to five pages from the sitemap and visibility=unlisted keeps the report out of the public directory, and both are part of Toolz Pro. Issue a key from the dashboard - key creation is part of Advanced, the tier that carries API access - and send it as a bearer token:

bash
curl -H "Authorization: Bearer tz_live_..." \
  "https://toolz.dev/api/ai-scan?url=example.com&mode=site"

A key is shown once, when it is created, and stored only as a hash - a lost key is revoked and replaced, not recovered. Revoking takes effect on the next call, and so does a plan change: the tier is read from the account at call time, not copied onto the key when it was issued.

Without a key, or with one whose plan does not cover the mode, the endpoint answers 403 with code: "PRO_REQUIRED" and a message naming what is missing.

What errors can it return?

Errors have success: false, an error message meant for people, and sometimes a code:

Status error When
400 Missing url parameter No url was sent
400 Invalid JSON body A POST body that is not JSON
400 A URL message, for example That address is not publicly reachable. The URL is invalid, not http or https, contains a username or password, or points at a private or local address
403 Private scans are part of Toolz Pro. Run it as a public scan, or upgrade at toolz.dev/pricing. with code: "PRO_REQUIRED" visibility=unlisted without a Pro sign-in
429 Too many scans. Wait a minute and try again. More than 6 scans from your IP address in 60 seconds
502 The scan could not be completed. The site may be unreachable. The scan failed unexpectedly on the server

Branch on the status and code, not on the message text, which may be reworded. The full list of URL messages is in Troubleshooting.

Are there limits?

The endpoint allows 6 scans per IP address in any 60-second window, counting refused requests too. There is no API key and no separate quota. Scans made through it are public unless a Pro token is sent with visibility=unlisted, and each one that loads the site is stored as a report. What the scanner fetches from the target is in Privacy and what gets fetched.