Skip to main content
SiteScan.top

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

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"])