---
title: "Scan a domain as several crawler identities and return a graded record"
method: POST
path: "/api/scan"
---

# Scan a domain as several crawler identities and return a graded record

`POST /api/scan`

## Request

- Base URL: `https://crawlcheck.io`
- URL: `https://crawlcheck.io/api/scan`
- Auth: none required

## Request body

- object
  - `domain` string, required: A bare domain or a full URL
  - `visibility` 'public' | 'unlisted' | 'private': unlisted: the report is reachable only by its link; the scan is sealed and archived but enters no public registry, dataset, history or resolve answer. private: needs a licence for the domain (or the owner); the report opens only for that licence holder or the owner and is not archived or listed anywhere (default public)

## Response `200`

The scan record

- ScanRecord
  - `id` string: Report id; the report renders at /r/{id} and the record at /r/{id}.json
  - `domain` string
  - `grade` 'A' | 'B' | 'C' | 'D' | 'F' | 'null', nullable: null when the origin refused CrawlCheck or the scan could not be graded
  - `overall` integer, nullable: AI visibility: reach, then read, then quote
  - `refused` boolean: The homepage and a path that cannot exist answered identically: a wall, not a site. Nothing is scored
  - `section_scores` object[]
    - `id` string
    - `title` string
    - `score` integer, nullable
  - `findings` object[]
    - `code` string
    - `severity` integer
    - `path` string
    - `title` string
    - `detail` string
    - `evidence` unknown
  - `self_audit` object: Invariants checked on this record itself; a finding here is a defect in CrawlCheck, never in the site

## Other responses

- `400`: Not a public domain (reserved names such as .example and .invalid included), or no JSON body
- `429`: Rate limited: 5 requests per 10 seconds per address; a scan of the same domain still running (code scan_in_progress); or the daily total of free scans reached (code daily_budget). Retry-After says when. A rescan of a domain scanned in the last 2 minutes returns that report (field cached) instead of fetching the site again; a domain where nothing answered returns 200 with unreachable: true, id: null and nothing stored
- `503`: Free scans paused (code scans_paused); keyed scans are unaffected

---

[API](https://skmtc.dev/crawlcheck/apis/crawlcheck-api.md) · [All operations](https://skmtc.dev/crawlcheck/apis/crawlcheck-api/llms.txt) · [OpenAPI document](https://skmtc.dev/crawlcheck/apis/crawlcheck-api/revisions/57acbd6d4489?raw)
