The public API.
What is documented here is the unauthenticated scan surface: the one behind the free scorecard. It is the part that is stable enough to build against.
Response envelope
Every response uses the same wrapper, success or failure.
{
"success": true,
"message": "Scan",
"data": { … },
"error": null,
"timestamp": "2026-08-12T06:00:00Z",
"requestId": "req_…"
}
On failure, success is false, data is null, and
error carries { code, details }. The one exception is rate limiting, which is
returned by the limiter ahead of the application and does not use the envelope: worth handling explicitly.
Endpoints
POST /api/v1/public/scans
Queue a scan. Returns 202 with a scan id: the result is not ready yet.
Request
{ "url": "https://example.com", "captchaToken": "…" } Response
{ "scanId": "scan_…", "status": "queued", "reused": false } Worth knowing
- Rate limited to 3 submissions an hour per caller.
- One real crawl per domain per day; inside that window
reusedcomes back true and you get that day’s scan. - CAPTCHA is verified before anything else, before URL validation, before any DNS lookup, and fails closed in production.
- The URL is checked against an SSRF guard. Addresses that resolve into private space are rejected.
GET /api/v1/public/scans/{scanId}
Read a scan. Poll until status leaves queued or running.
Response
{ "status": "done", "score": 71, "grade": "C+",
"categories": [{ "name": "…", "score": 62 }],
"findingsCount": 12, "pagesScanned": 10, "pagesTotal": 47,
"remainingPages": 37, "unlocked": false,
"advisory": "Advisory scan of public HTML only." } Worth knowing
findingsis omitted entirely until an email is verified: omitted, not blanked, because a locked field still present in the payload is one devtools tab away from unlocked.- A scan that never ran has no grade at all rather than a default one.
- No traffic metrics are returned. There are no clicks, impressions, positions or citation counts in this payload.
GET /api/v1/public/scorecards/{slug}
Read a shared scorecard by its share slug.
Response
Same shape as a scan, always with `unlocked: false`. Worth knowing
- Always locked, even if the owner has verified their email: otherwise one verified address would publish the findings to anyone holding the link.
- Expires 30 days after the scan.
POST /api/v1/public/scans/{scanId}/claim
Trade an email address for the findings. Sends a verification link.
Request
{ "email": "you@example.com" } Response
202 accepted Worth knowing
- Rate limited separately from scan submission, because it sends mail rather than starting a crawl.
Error codes
| Code | Meaning |
|---|---|
CAPTCHA_REQUIRED | The verification challenge was not solved. Fails closed in production. |
VALIDATION_FAILED | The URL was rejected: malformed, or resolving somewhere it should not. |
SERVICE_UNAVAILABLE | The scanning engine is not available. No grade is invented in its absence. |
RESOURCE_NOT_FOUND | Unknown scan id, or a scorecard slug that has expired. |
429 | Rate limited. Handled outside the envelope, so parse defensively. |
A worked example
# queue
curl -sX POST https://api.crawld.co/api/v1/public/scans \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com"}'
# poll until status is done or failed
curl -s https://api.crawld.co/api/v1/public/scans/scan_abc123 The authenticated API Not documented yet
The brand-scoped surface: fix queue, audits, watchdog, citations: exists but is not publicly documented, which means it is not stable enough to build against and should not be treated as though it were. When it is documented it will be here.
Webhooks and the platform integrations are described on the integrations pages, including which of them are built.