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 402 with the number used, the limit, and the reset date.
  • · GET /api/v1/usage is 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.