API reference
CRO9 API
Two engines over HTTP: whether six named AI crawlers can read a page, and a 33-point CRO and AI-readiness scan. Free for the first 100 calls a month.
Authentication
Create a key in your dashboard under API Keys. It is shown once — we store a SHA-256 digest, so it cannot be shown again.
Authorization: Bearer cro9_sk_... # or X-Api-Key: cro9_sk_...
Billing and limits
- · Free tier: 100 calls a month, 10 a day.
- · Only successful calls count. A 4xx or 5xx is never billed and never counts against your quota.
- · Every response carries
X-CRO9-Request-Id. It is the idempotency key, so a retry cannot bill twice. - · Over the limit returns
402with the number used, the limit, and the reset date. - ·
GET /api/v1/usageis free and never counts against your quota.
POST/api/v1/ai-visibility
Probes six AI crawlers against a real-browser control and classifies the CAUSE of any refusal. This is the endpoint no other API offers.
curl -X POST https://www.cro9.com/api/v1/ai-visibility \
-H "Authorization: Bearer $CRO9_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'{
"ok": true,
"request_id": "ee700bbc-...",
"url": "https://example.com/",
"score": 67, // 0-100, or null when the browser control failed
"reachable": true, // false => the findings are about OUR access
"control": { "status": 200, "words": 1248 },
"crawlers": [
{
"label": "ChatGPT (training)",
"verdict": "disallowed", // ok | blocked | disallowed | empty | error
"status": 200,
"words": 1248,
"robotsAllowed": false,
"cause": "robots_disallow",
"causeWho": "your robots.txt", // WHO refused - the actionable field
"causeDetail": "Disallow: / under User-agent: GPTBot"
}
]
}score is null, not zero, when the browser control could not reach the site. Null means we could not tell; zero would mean we looked and found nothing readable. Rendering them the same way is the most common mistake integrators make here.
POST/api/v1/scan
The 33-check CRO and AI-readiness scan. Pass checks:'summary' for the score and issue count without the itemised findings.
curl -X POST https://www.cro9.com/api/v1/scan \
-H "Authorization: Bearer $CRO9_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","checks":"full"}'{
"ok": true,
"score": 96,
"grade": "A", // null when the page could not be read
"reachable": true,
"categories": { "ai_access": { "earned": 8, "max": 10 } },
"issue_count": 1, // summary mode
"checks": [ // full mode only
{
"id": "meta_description",
"status": "fail", // pass | warn | fail | na
"points": 0, "max": 3,
"note": "171 characters; the spec is 150-160",
"fix": "Rewrite to 150-160 characters ending in an action"
}
]
}A check with status na is excluded from the denominator, not scored as a failure. A page with no images is not penalised for missing alt text.
GET/api/v1/usage
What you have used, your limit, and when it resets. Free, and it still answers when you are over the limit — which is the case it exists for.
{
"ok": true,
"tier": "free",
"month": { "used": 12, "limit": 100, "resets": "2026-10-01" },
"day": { "used": 3, "limit": 10, "resets": "00:00 UTC" },
"metered": false // true = no cap, billed per call
}Errors
401 missing, invalid or revoked key 400 the request was malformed - never billed 402 quota reached; body carries used, limit and reset 502 the target site or an upstream failed - never billed
Cold scans complete in 0.28–0.47 seconds, measured against live production. Eight outbound requests run in parallel: six crawler identities, a browser control, and robots.txt.
Free to start
Get a key and make the first call
100 calls a month, no card. The scan endpoint runs the same engine as the public checker, so you can compare the response to what the website shows before you build on it.