SiteScan.top API
Start scans and read reports from your own code: a CI check before a release, a dashboard across client sites, an alert when a score drops. The API runs the same scanner as the site and is paid from the same token balance.
Base URL: https://sitescan.top/api/v1
Quick start
- Sign in, open the account panel and go to the API tab. Create a key and copy it — it is shown once.
- Start a scan with POST /scans and the domain. The answer is 202 with the scan's id.
- Poll GET /scans/{id} every few seconds until status is done or failed. Add ?report=1 to get the full report with the last answer.
Authentication
Every request except /ping carries a key, in either header: Authorization: Bearer <key> or X-API-Key: <key>.
Keys start with ssk_. Only a hash of the key is stored, so a lost key cannot be shown again — revoke it and create a new one. An account can hold 5 keys.
The key is the credential: keep it on a server or in CI secrets, never in a public web page.
Tokens and pricing
A scan costs one token per page the crawl actually read — the same price as a check from the account panel. The tokens are taken when the scan finishes.
A scan that fails, times out or is blocked by the site costs nothing. Reading reports, the scan list and the account costs nothing.
A scan needs at least one token, and never reads more pages than the balance pays for. Tokens are bought on the Pricing page.
Limits
60 requests a minute per key. Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a 429 also carries Retry-After.
3 scans running at once per account. A second scan of a domain that is already being scanned answers with the running one instead of starting another.
pages is 1 to max_pages (GET /account says how many this server allows), 5 when it is left out.
Endpoints
GET /ping
Whether the API is up. Answers 503 when the account store is unavailable. Needs no key — this is what the status page calls.
curl https://sitescan.top/api/v1/ping
GET /account
The account behind the key: balance in tokens, and the limits that apply to it.
curl https://sitescan.top/api/v1/account \
-H "Authorization: Bearer $SITESCAN_KEY"
POST /scans
Start a scan. Answers 202 while the crawl runs, or 200 when it finished within the request.
curl -X POST https://sitescan.top/api/v1/scans \
-H "Authorization: Bearer $SITESCAN_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com", "pages": 5}'
GET /scans/{id}
One scan. 202 while it runs (with progress), 200 once it is done or failed. The poll is also what moves a stalled crawl along.
curl "https://sitescan.top/api/v1/scans/scn_4f1c9a2b7d3e8f60?report=1" \
-H "Authorization: Bearer $SITESCAN_KEY"
GET /scans
The 50 latest scans this account started through the API, newest first.
curl https://sitescan.top/api/v1/scans \
-H "Authorization: Bearer $SITESCAN_KEY"
GET /reports/{domain}
The latest stored report for a domain, whoever started it. Free.
curl https://sitescan.top/api/v1/reports/example.com \
-H "Authorization: Bearer $SITESCAN_KEY"
Scan status
- running
- The crawl is in the queue or under way. progress.stage is queued, crawl, audit or score.
- done
- The report is stored. score, pages_read and tokens_charged are filled in.
- failed
- No report — error says why (for example timeout or blocked). Nothing was charged.
Errors
Errors share one shape: {"error": {"code": "…", "message": "…"}}. Branch on code; the message is for people.
- key_required (HTTP 401)
- No key in the request.
- key_invalid (HTTP 401)
- The key is wrong or has been revoked.
- insufficient_tokens (HTTP 402)
- The balance is empty — top up in the account panel.
- unknown_scan (HTTP 404)
- No scan with this id on this account.
- unknown_report (HTTP 404)
- No stored report for this domain yet.
- unknown_endpoint (HTTP 404)
- No such endpoint.
- method_not_allowed (HTTP 405)
- The endpoint exists, but not with this method.
- invalid_domain (HTTP 422)
- domain is not a domain name.
- invalid_pages (HTTP 422)
- pages is out of range.
- rate_limited (HTTP 429)
- Too many requests this minute — wait for Retry-After.
- too_many_scans (HTTP 429)
- Three scans are already running on this account.
- store_unavailable (HTTP 503)
- A problem on our side; try again later.
Examples
curl + jq
export SITESCAN_KEY="ssk_…"
ID=$(curl -s -X POST https://sitescan.top/api/v1/scans \
-H "Authorization: Bearer $SITESCAN_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}' | jq -r .scan.id)
while :; do
SCAN=$(curl -s https://sitescan.top/api/v1/scans/$ID -H "Authorization: Bearer $SITESCAN_KEY")
STATUS=$(echo "$SCAN" | jq -r .scan.status)
[ "$STATUS" != "running" ] && break
sleep 5
done
echo "$SCAN" | jq .scan
JavaScript
const KEY = process.env.SITESCAN_KEY;
const api = (path, init = {}) =>
fetch("https://sitescan.top/api/v1" + path, {
...init,
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
}).then((r) => r.json());
let { scan } = await api("/scans", { method: "POST", body: JSON.stringify({ domain: "example.com", pages: 5 }) });
while (scan.status === "running") {
await new Promise((done) => setTimeout(done, 5000));
({ scan } = await api(`/scans/${scan.id}`));
}
console.log(scan.status, scan.score, scan.report_url);
Python
import os, time, requests
API = "https://sitescan.top/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SITESCAN_KEY']}"}
scan = requests.post(f"{API}/scans", json={"domain": "example.com", "pages": 5}, headers=HEADERS).json()["scan"]
while scan["status"] == "running":
time.sleep(5)
scan = requests.get(f"{API}/scans/{scan['id']}", headers=HEADERS).json()["scan"]
print(scan["status"], scan["score"], scan["report_url"])