docs / endpoint
POST /v1/site/audit
Crawl and audit a whole site; billed per delivered page (sum of enabled checks x pages delivered).
Credits: 2 credits per delivered page per enabled check -- billed on what's actually delivered, never a flat rate.
Timeout: 600s
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
domain |
string | required | — | Domain or URL to crawl -- the hostname is extracted from either form. Required, max 253 chars. |
max_pages |
integer | required | — | Maximum number of pages to crawl before stopping. 1-500. |
checks |
array | optional | ["crawl"] |
Which checks to run per crawled page. Optional, default ["crawl"]. Duplicates removed, order preserved. Array of: crawl, lighthouse, accessibility. |
device |
string | optional | "mobile" |
One of: mobile, desktop. Optional, default "mobile". |
Request
→ requestPOST /v1/site/audit
POST /v1/site/audit HTTP/1.1
Authorization: Bearer sof_live_your_key_here
Content-Type: application/json
{
"domain": "example.com",
"max_pages": 5,
"checks": [
"crawl",
"lighthouse"
],
"device": "mobile"
}
Response
Wrapped in the standard envelope -- id, object, created_at, elapsed_ms, cache, credits -- documented once on The Contract. The body below is a real envelope with this endpoint's data shape; values vary per request, and a … marks an array cut short for display.
← response200
{
"id": "site_5sunoif4ajelb7hud3biu4dd",
"request_id": "req_6wkqmjzhgbdmfhhqer7roiz2re",
"object": "site_audit",
"created_at": "2026-08-09T07:21:20Z",
"elapsed_ms": 180,
"cache": "miss",
"credits": {
"charged": 12,
"balance": 9857
},
"data": {
"domain": "example.com",
"pages_requested": 5,
"pages_crawled": 4,
"pages_delivered": 3,
"credits_charged": 12,
"pages": [
{
"url": "https://example.com/",
"status": "ok",
"crawl": {
"http_status": 200,
"links": {
"internal": 2,
"external": 0
}
},
"lighthouse": {
"device": "mobile",
"scores": {
"performance": 90,
"accessibility": 91,
"best_practices": 93,
"seo": 80
}
}
},
"…"
],
"summary": {
"score": {
"value": 82,
"band": "excellent",
"grade": "A"
},
"axes": [
{
"key": "speed",
"score": 73,
"band": "good"
}
],
"issues": {
"critical": 2,
"major": 1,
"minor": 1
},
"broken_links": 1,
"pages_with_errors": 1
}
}
}
Response fields
What each field in data (above) means.
| Field | Description |
|---|---|
data.domain |
The resolved hostname that was crawled. |
data.pages_requested |
max_pages from the request, echoed back. |
data.pages_crawled |
Number of pages the crawl actually visited (can be less than pages_requested on a smaller site, never more). |
data.pages_delivered |
Number of pages that passed every enabled check -- the billing unit. |
data.credits_charged |
Actual credits charged for this call: pages_delivered times the enabled checks' per-page cost -- 0 on a replay, since a replay is never re-billed. Always equal to the envelope's own credits.charged. |
data.pages |
One row per crawled page, whether delivered or not. |
data.pages[].url |
The page URL. |
data.pages[].status |
"ok" if every enabled check passed for this page, "error" otherwise -- an error page isn't billed. |
data.pages[].crawl |
This page's crawl-check result, when the crawl check is enabled and passed -- same shape as the page/crawl endpoint's data. |
data.pages[].crawl.http_status |
HTTP status code for this page. |
data.pages[].crawl.links |
Outbound link counts for this page. |
data.pages[].crawl.links.internal |
Links pointing at the same site. |
data.pages[].crawl.links.external |
Links pointing off-site. |
data.pages[].lighthouse |
This page's lighthouse-check result, when the lighthouse check is enabled and passed -- same scores/metrics shape as the page/lighthouse endpoint's data, but without that endpoint's audits/screenshots (kept lean per-page so a multi-page site audit doesn't balloon). |
data.pages[].lighthouse.device |
Device profile the audit ran under. |
data.pages[].lighthouse.scores |
The same four 0-100 category scores as the standalone lighthouse endpoint. |
data.summary |
Site-wide rollup computed across every delivered page. |
data.summary.score |
Overall site score. |
data.summary.score.value |
0-100 overall score. |
data.summary.score.band |
"excellent" (80+), "good" (50+), or "improve" (below 50). |
data.summary.score.grade |
Letter grade A-F derived from value. |
data.summary.axes |
Per-dimension scores (speed, accessibility) for whichever checks were enabled. |
data.summary.axes[].key |
Which dimension this axis measures, e.g. "speed". |
data.summary.axes[].score |
0-100 score for this axis. |
data.summary.axes[].band |
Same excellent/good/improve banding as score.band, for this axis. |
data.summary.issues |
Accessibility issues found, bucketed by severity (only populated when the accessibility check is enabled). |
data.summary.issues.critical |
Count of critical-severity issues. |
data.summary.issues.major |
Count of major-severity issues. |
data.summary.issues.minor |
Count of minor-severity issues. |
data.summary.broken_links |
Count of internal links returning a non-2xx status, across delivered pages. |
data.summary.pages_with_errors |
Count of pages that failed to load cleanly or failed the crawl check. |