A site sent twice gets one item. A site without a stored result, or an input that isn’t a domain, gets an error in its place, and the other sites are still returned. It never starts a scan.
Credits
Each result found costs its checks’ stored price, up to 3 each; sites without a result cost nothing. The batch is charged as a whole: if your credits don’t cover every result found, the request is refused and nothing is charged.
Request
curl -X POST "https://api.checkanysite.com/v1/sites/batch" \
-H "Authorization: Bearer $CHECKANYSITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domains": ["stripe.com", "lovable.dev"], "checks": ["ai", "tech"]}'from checkanysite import CheckAnySite
client = CheckAnySite() # Reads the key from CHECKANYSITE_API_KEY.
batch = client.sites.batch(["stripe.com", "lovable.dev"], checks=["ai", "tech"])const url = 'https://api.checkanysite.com/v1/sites/batch';
const response = await fetch(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHECKANYSITE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
domains: ['stripe.com', 'lovable.dev'],
checks: ['ai', 'tech'],
}),
});
const batch = await response.json();Parameters
Idempotency-KeystringheaderA unique value for this request, such as a UUID. A retry with the same key within 24 hours returns the first response (
Idempotent-Replayed: true), and never starts a second scan or charges twice. The same key with a different request returnsidempotency_key_reused.
Request body
JSON, with Content-Type: application/json. A field we don’t know is refused, so a typo never passes silently.
domainsarray of stringrequiredbody1 to 50 domains, or URLs whose host is used. A domain sent twice is answered once, in its first place.
checksarray of one of: ai, readiness, popularity, tech, dns, trust, summary, historybodyThe checks to answer for each domain; left out, every check.
maxAgeDaysintegerbodyAnswer only with results at most this many days old; older ones are
no_result.
Response
| Status | Body | When |
|---|---|---|
| 200 OK | SiteBatch | A result or an error for each site. |
{
"data": [
{
"input": "wikipedia.org",
"domain": "wikipedia.org",
"result": {
"domain": "wikipedia.org",
"checks": [
"ai"
],
"scanId": "0b5a7a6e-1f7e-4d7c-9a4e-2f0d8b1c6a11",
"scannedAt": "2026-11-10T09:30:06.000Z",
"complete": true,
"pending": [],
"limited": null,
"finalUrl": "https://www.wikipedia.org/",
"resultUrl": "https://checkanysite.com/site/wikipedia.org?focus=ai",
"versions": {
"signatures": "0.6.1",
"score": "1.1"
},
"ai": {
"level": "none",
"builders": [],
"patterns": []
},
"readiness": null,
"popularity": null,
"tech": null,
"dns": null,
"trust": null,
"score": null,
"summary": null,
"history": null,
"notes": []
}
},
{
"input": "example.org",
"domain": "example.org",
"error": {
"code": "no_result",
"message": "We have no result for example.org yet. Scan it with POST /v1/scans."
}
}
]
}dataarray of valueOne item per domain: its
result, or anerror.
Errors
| Status | Code | What happened |
|---|---|---|
| 400 | invalid_json | Invalid JSON |
| 400 | invalid_parameter | Invalid parameter |
| 415 | unsupported_media_type | Unsupported content type |
| 413 | request_too_large | Body too large |
| 401 | missing_api_key | No API key |
| 401 | invalid_api_key | Unknown API key |
| 401 | revoked_api_key | Revoked API key |
| 401 | expired_api_key | Expired API key |
| 429 | rate_limited | Too many requests |
| 422 | idempotency_key_reused | Idempotency key reused |
| 409 | idempotency_key_in_use | Request in progress |
| 429 | quota_exceeded | Monthly credits used up |
| 429 | key_limit_reached | Key limit reached |
| 500 | internal_error | Internal error |