API Documentation
Use the API to read your sites, scans, and findings. Trigger scans and get alerts when things change. Works on Pro and Studio plans.
All calls use JSON. Each response has a request ID you can use for support. Set up takes less than a minute.
Quickstart
Get your first scan result in 60 seconds. Create an API key in Settings, then run these three commands:
1. List your sites
curl -H "Authorization: Bearer sc_live_YOUR_KEY" \ https://sitecurl.com/api/v1/sites
2. Trigger a scan
curl -X POST \ -H "Authorization: Bearer sc_live_YOUR_KEY" \ https://sitecurl.com/api/v1/sites/SITE_ID/scans
3. Get findings
curl -H "Authorization: Bearer sc_live_YOUR_KEY" \ https://sitecurl.com/api/v1/scans/SCAN_ID/findings
Every response has X-Request-ID and X-RateLimit-* headers so you can track usage.
Authentication
Every request needs a Bearer token in the Authorization header:
Authorization: Bearer sc_live_...
Create your API key in Settings. Open the API tab to get started.
Rate limits
| Plan | Requests per hour |
|---|---|
| Pro | 100 |
| Studio | 1,000 |
Each response includes these headers:
X-Request-ID: a unique ID for each request (send your own or we make one)X-RateLimit-Limit: max requests per hourX-RateLimit-Remaining: how many you have leftX-RateLimit-Reset: when the window resets (Unix time)Retry-After: seconds to wait (only on 429 responses)
Sites
List sites
GET /api/v1/sites
Returns all sites on your account.
Pass page (default 1) and per_page (default 25, max 100) to paginate.
{
"data": [
{
"id": "uuid",
"name": "example.com",
"url": "https://example.com/",
"status": "active",
"scan_frequency": "weekly",
"last_scanned_at": "2026-03-18T12:00:00Z",
"created_at": "2026-01-15T10:30:00Z"
}
],
"meta": { "page": 1, "per_page": 25, "has_more": false }
}
Get site
GET /api/v1/sites/:id
Returns one site and its latest scan.
Scans
List scans
GET /api/v1/sites/:site_id/scans
Get scan
GET /api/v1/scans/:id
Response fields:
id,site_id,status,scorechecks_total,passed_count,critical_count,warning_countpages_scanned,delta_fixed_count,delta_regressed_countstarted_at,completed_at
Trigger scan
POST /api/v1/sites/:site_id/scans
Returns 202. The scan runs in the background. Use webhooks or poll the scan endpoint to know when it is done.
Returns 409 if a scan is still running. Returns 429 if you hit the daily cap.
Findings
List findings for a scan
GET /api/v1/scans/:scan_id/findings
Optional filters:
category: one of the API filter keys listed below
| API key | Category |
|---|---|
seo | SEO |
performance | speed |
security | security |
accessibility | accessibility |
technical | technical health |
availability | uptime |
ai_search_readiness | AI readiness |
severity: critical, warning, infopassed: true or falsecheck_key: filter by specific check keypage_url: filter by page URLpage(default 1),per_page(default 50, max 200)
Each finding has its category, severity, message, fix, page URL, and proof.
Reports
Get report
GET /api/v1/reports/:token
Returns the report token, site, scan, and expiry date.
Intelligence
Trends and changes you can only see over time.
Score trends
GET /api/v1/sites/:id/intelligence/trends?limit=20
Returns past scores, oldest first.
Regressions
GET /api/v1/sites/:id/intelligence/regressions
Checks that broke since the last scan, plus new issues.
Improvements
GET /api/v1/sites/:id/intelligence/improvements
Checks that were fixed since the last scan.
Priority inbox
GET /api/v1/intelligence/priority
Your top 5 action items across all sites. Covers score drops, new issues, quick wins, and stale scans.
Webhooks
Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/webhook_endpoints |
List all endpoints |
POST |
/api/v1/webhook_endpoints |
Create endpoint |
PATCH |
/api/v1/webhook_endpoints/:id |
Update endpoint |
DELETE |
/api/v1/webhook_endpoints/:id |
Delete endpoint |
Create request body
{ "url": "https://...", "events": ["scan_completed"] }
Available events: scan_completed, score_dropped, critical_regression.
Payload headers
X-SiteCurl-Event: event typeX-SiteCurl-Signature: HMAC-SHA256 signature (v1=<hex>)X-SiteCurl-Timestamp: Unix timestamp
To check it: sign "#{timestamp}.#{body}" with your secret key.
Signature verification examples
Ruby
timestamp = request.headers["X-SiteCurl-Timestamp"]
signature = request.headers["X-SiteCurl-Signature"]
body = request.body.read
expected = "v1=" + OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{body}")
unless ActiveSupport::SecurityUtils.secure_compare(expected, signature)
head :unauthorized and return
end
Node.js
const crypto = require("crypto");
function verify(secret, timestamp, body, signature) {
const expected = "v1=" + crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected), Buffer.from(signature)
);
}
Python
import hmac, hashlib
def verify(secret, timestamp, body, signature):
expected = "v1=" + hmac.new(
secret.encode(), f"{timestamp}.{body}".encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
Example: scan_completed
{
"event": "scan_completed",
"site_id": "uuid",
"scan_id": "uuid",
"score": 85,
"checks_total": 80,
"passed_count": 68,
"critical_count": 2,
"warning_count": 10,
"report_token": "abc123..."
}
Example: score_dropped
{
"event": "score_dropped",
"site_id": "uuid",
"scan_id": "uuid",
"current_score": 72,
"previous_score": 85,
"drop": 13
}
Example: critical_regression
{
"event": "critical_regression",
"site_id": "uuid",
"scan_id": "uuid",
"critical_checks": ["https_redirect", "mixed_content"]
}
MCP (AI tools)
Connect AI coding tools like Claude Code, Cursor, or Windsurf to SiteCurl. Uses the same API key.
Endpoint
POST /api/v1/mcp
Speaks JSON-RPC 2.0. Three methods: initialize, tools/list, tools/call. Supports Streamable HTTP transport: send Accept: text/event-stream to receive SSE-formatted responses.
Setup
Add to your Claude Code or Cursor settings:
{
"mcpServers": {
"sitecurl": {
"url": "https://sitecurl.com/api/v1/mcp",
"headers": {
"Authorization": "Bearer sc_live_..."
}
}
}
}
Available tools
| Tool | Description |
|---|---|
sitecurl_list_sites |
List all monitored websites |
sitecurl_get_site |
Site details with latest scan |
sitecurl_scan |
Trigger a new scan |
sitecurl_scan_status |
Check status of latest scan |
sitecurl_findings |
Scan findings (filterable) |
sitecurl_score |
Current health score |
sitecurl_trends |
Score history over time |
sitecurl_priority |
Top action items across all sites |
sitecurl_regressions |
Checks that broke since last scan |
sitecurl_improvements |
Checks fixed since last scan |
sitecurl_add_site |
Add a new website to monitor |
CI/CD integration
Block deploys when your site has critical audit findings. Install the @sitecurl/mcp npm package and run sitecurl-check in your pipeline.
Install
npm install -g @sitecurl/mcp
Usage
sitecurl-check https://example.com --fail-on=critical
Triggers a scan, waits for it to finish, then exits with code 0 (pass) or 1 (findings above your threshold). Set the API key via the SITECURL_API_KEY environment variable.
Exit codes
| Code | Meaning |
|---|---|
| 0 | No findings at or above threshold |
| 1 | Findings found, check failed |
| 2 | Scan failed or timed out |
| 3 | Setup error (bad key, missing site) |
GitHub Actions
- name: SiteCurl Audit
run: npx @sitecurl/mcp check https://example.com --fail-on=critical
env:
SITECURL_API_KEY: ${{ secrets.SITECURL_API_KEY }}
GitLab CI
sitecurl-audit:
script:
- npx @sitecurl/mcp check https://example.com --fail-on=critical
variables:
SITECURL_API_KEY: $SITECURL_API_KEY
Error responses
Errors come back as JSON with a message, code, and request ID:
{
"error": "Rate limit exceeded",
"error_code": "RATE_LIMIT_EXCEEDED",
"request_id": "a1b2c3d4-..."
}
| Status | Error code | Meaning |
|---|---|---|
| 401 | INVALID_API_KEY |
Invalid or missing API key |
| 403 | PLAN_REQUIRED |
Plan does not include API or webhook access |
| 404 | NOT_FOUND |
Resource not found |
| 409 | SCAN_IN_PROGRESS |
A scan is already running for this site |
| 422 | VALIDATION_ERROR |
Invalid request parameters |
| 429 | RATE_LIMIT_EXCEEDED |
Hourly rate limit exceeded (includes Retry-After header) |
| 429 | QUOTA_EXCEEDED |
Daily scan quota exceeded |
Machine-readable spec
Download the full spec at /openapi.yaml (OpenAPI 3.1.0).
Questions?
Get in touch if anything is unclear.