API Documentation
Integrate CompliaScan into your development workflow with our REST API. Programmatically create scans, retrieve results, enforce CI/CD quality gates, and receive real-time webhook notifications.
Base URL: https://compliascan.com/api/v1
Authentication
All API requests require a Bearer token in the Authorization header. API keys are prefixed with cs_ and can be generated from your dashboard settings.
Rate Limits: Endpoints are rate-limited per plan — Pro: 200/min, Business: 1000/min. API access requires a Pro or Business plan. All responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Exceeding the limit returns 429 with a Retry-After header.
Endpoints
/api/v1/scanCreate a new accessibility scan. The scan runs asynchronously and returns immediately with a scan ID you can use to poll for results.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The URL to scan. Must be a valid, publicly accessible URL. |
multiPage | boolean | No | When true, crawls and scans linked pages from the root URL. Defaults to false. |
callbackUrl | string | No | A webhook URL to receive a POST notification when the scan completes. |
waitForSelector | string | No | CSS selector to wait for before scanning. Useful for SPAs that load content dynamically. |
waitTime | number | No | Additional wait time in milliseconds (0-10000) after page load before scanning. |
Response 202 Accepted
{
"scanId": "scn_a1b2c3d4e5f6",
"status": "pending",
"statusUrl": "/api/v1/scan/scn_a1b2c3d4e5f6"
}/api/v1/scan/{scanId}Retrieve the results of a completed scan. While the scan is still running, the response will include "status": "running". Poll this endpoint until the status changes to "completed".
Response 200 OK
{
"id": "scn_a1b2c3d4e5f6",
"url": "https://example.com",
"status": "completed",
"score": 72,
"totalIssues": 18,
"criticalCount": 2,
"seriousCount": 5,
"moderateCount": 7,
"minorCount": 4,
"rulesChecked": 108,
"rulesPassed": 90,
"pageTitle": "Example Domain",
"scanDurationMs": 12450,
"pagesScanned": 1,
"pagesTotal": 1,
"createdAt": "2026-02-15T10:30:00Z",
"completedAt": "2026-02-15T10:30:12Z",
"pdfUrl": "https://compliascan.com/api/v1/scan/scn_a1b2c3d4e5f6/pdf",
"issues": [
{
"ruleId": "color-contrast",
"impact": "serious",
"description": "Elements must have sufficient color contrast",
"helpText": "Ensure foreground and background colors meet WCAG 2 AA minimum contrast ratio thresholds.",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.10/color-contrast",
"wcagCriteria": ["1.4.3"],
"nodeCount": 3,
"affectedNodes": [
{
"html": "<p class=\"light-text\">...</p>",
"selector": ".hero > p.light-text",
"failureSummary": "Element has insufficient color contrast of 2.8:1 (foreground: #999999, background: #ffffff, expected ratio: 4.5:1)"
}
]
}
]
}/api/v1/scan/{scanId}/gateCI/CD quality gate endpoint. Returns a pass or fail verdict based on configurable thresholds. Use this in your deployment pipeline to block releases that introduce accessibility regressions.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
threshold | number | Minimum accessibility score to pass (0-100). Defaults to 70. |
maxCritical | number | Maximum critical issues allowed before failing. Optional. |
maxSerious | number | Maximum serious issues allowed before failing. Optional. |
Response 200 OK202 Accepted
202 Accepted with "status": "pending" or "status": "running". Poll until you receive a 200 OK with the full gate verdict.{
"passed": true,
"score": 92,
"threshold": 70,
"gates": {
"score": { "passed": true, "actual": 92, "threshold": 70 },
"critical": { "passed": true, "actual": 0, "threshold": 1 }
},
"summary": {
"totalIssues": 5,
"critical": 0,
"serious": 2,
"moderate": 2,
"minor": 1
},
"scanUrl": "https://compliascan.com/scan/scn_a1b2c3d4e5f6"
}/api/v1/exportExport all scan data for your account as JSON. Includes full scan results and monitored sites. Useful for data warehousing, custom reporting, or backing up your compliance history. Returns up to 500 most recent scans.
Response 200 OK
{
"exportedAt": "2026-02-15T12:00:00Z",
"userId": "user_abc123",
"plan": "pro",
"scans": [
{
"id": "scn_a1b2c3d4e5f6",
"url": "https://example.com",
"status": "completed",
"score": 72,
"totalIssues": 18,
"criticalCount": 2,
"seriousCount": 5,
"moderateCount": 7,
"minorCount": 4,
"rulesChecked": 108,
"rulesPassed": 90,
"createdAt": "2026-02-15T10:30:00Z",
"completedAt": "2026-02-15T10:30:12Z",
"results": [...]
}
],
"sites": [
{
"id": "uuid",
"domain": "example.com",
"name": "Example Site",
"lastScore": 85,
"monitoringEnabled": true,
"monitoringFrequency": "weekly"
}
],
"totalScans": 142,
"totalSites": 3
}/api/v1/scan/{scanId}/fixPro+Generate fix instructions for all violations in a completed scan. Uses your BYOK API key (OpenAI, Anthropic, or Gemini) if configured, otherwise returns deterministic rule-based suggestions.
Response 200 OK
{
"fixes": [
{
"scanResultId": "uuid",
"ruleId": "color-contrast",
"impact": "serious",
"instruction": "Increase the contrast ratio...",
"codeSnippet": "<!-- Before -->\n<p style=\"color: #999\">...\n<!-- After -->\n<p style=\"color: #595959\">...",
"provider": "deterministic",
"model": "rule-templates-v1",
"nodeFixes": [
{
"html": "<p style=\"color: #999\">Light text</p>",
"selector": ".hero > p",
"before": "<p style=\"color: #999\">",
"after": "<p style=\"color: #595959\">"
}
]
}
],
"count": 18
}/api/v1/scan/{scanId}/fix/{resultId}Pro+Get a specific fix instruction for a single scan result. Returns the cached fix if already generated.
Response 200 OK
{
"fix": {
"id": "uuid",
"scanResultId": "uuid",
"ruleId": "image-alt",
"impact": "critical",
"instruction": "Add descriptive alt text...",
"codeSnippet": "<img src=\"logo.png\" alt=\"Company logo\" />",
"provider": "openai",
"model": "gpt-4o-mini",
"createdAt": "2026-02-15T10:30:00Z",
"nodeFixes": [
{
"html": "<img src=\"logo.png\">",
"selector": "header > img",
"before": "<img src=\"logo.png\">",
"after": "<img src=\"logo.png\" alt=\"Company logo\">"
}
]
}
}/api/v1/sites/{siteId}/historyGet score history for a monitored site. Useful for tracking accessibility improvements over time.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
days | number | Number of days of history to return (1-365). Defaults to 30. |
Response 200 OK
{
"history": [
{
"score": 78,
"totalIssues": 12,
"criticalCount": 1,
"seriousCount": 3,
"recordedAt": "2026-02-15T10:30:00Z"
},
{
"score": 82,
"totalIssues": 9,
"criticalCount": 0,
"seriousCount": 2,
"recordedAt": "2026-02-08T10:30:00Z"
}
],
"days": 30
}/api/v1/sites/{siteId}/alertsGet monitoring alert configurations for a site. Alerts trigger email notifications when accessibility metrics change.
Response 200 OK
{
"alerts": [
{
"alertType": "score_drop",
"threshold": 5,
"enabled": true,
"notifyEmail": "dev@example.com"
},
{
"alertType": "new_critical",
"enabled": true,
"notifyEmail": "dev@example.com"
}
]
}/api/v1/sitesList all monitored sites for your account, including domain, monitoring settings, and last scan score.
Response 200 OK
{
"sites": [
{
"id": "uuid",
"domain": "example.com",
"name": "Example Site",
"lastScore": 85,
"lastScannedAt": "2026-02-15T10:30:00Z",
"monitoringEnabled": true,
"monitoringFrequency": "weekly",
"createdAt": "2026-01-01T00:00:00Z"
}
]
}/api/v1/sitesAdd a new monitored site. The domain is normalized automatically (protocol and www prefix are stripped).
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
domain | string | Yes | The domain to monitor (e.g. "example.com"). |
name | string | No | A display name for the site. Defaults to the domain. |
Response 201 Created
{
"site": {
"id": "uuid",
"domain": "example.com",
"name": "Example Site"
}
}/api/v1/sites/{siteId}Get detailed information about a specific site, including the latest scan results summary.
Response 200 OK
{
"site": {
"id": "uuid",
"domain": "example.com",
"name": "Example Site",
"lastScore": 85,
"monitoringEnabled": true,
"monitoringFrequency": "weekly"
},
"latestScan": {
"id": "uuid",
"score": 85,
"totalIssues": 5,
"criticalCount": 0,
"seriousCount": 2,
"completedAt": "2026-02-15T10:30:00Z"
}
}/api/v1/user/usageGet your current plan usage metrics including scan counts, site usage, and billing period information.
Response 200 OK
{
"plan": "pro",
"scansUsed": 42,
"scanLimit": -1,
"sitesUsed": 3,
"siteLimit": 15,
"pagesScanned": 210,
"pagesPerScan": 500,
"onDemandUsed": 5,
"onDemandLimit": 25,
"periodStart": "2026-01-20T00:00:00Z",
"periodEnd": "2026-02-19T00:00:00Z"
}/api/v1/scan/{scanId}/pdfPro+Download a PDF accessibility report for a completed scan. Business plan users receive white-labeled reports. Returns the PDF file directly as a binary download.
Response 200 OKContent-Type: application/pdf
Returns the PDF file as a binary attachment. The Content-Disposition header includes the filename.
/api/v1/scan/{scanId}/rescanTrigger a rescan of a previously scanned URL. Uses your on-demand scan quota. The original scan must be completed or failed.
Response 202 Accepted
{
"scanId": "new-scan-uuid",
"originalScanId": "original-scan-uuid",
"status": "pending",
"statusUrl": "/api/v1/scan/new-scan-uuid"
}/api/v1/scan/{scanId}/roadmapPaidGet a prioritized remediation roadmap for a completed scan. Returns violations grouped by priority with estimated effort and WCAG criteria references.
Response 200 OK
{
"phases": [
{
"name": "Critical Fixes",
"priority": 1,
"items": [
{
"ruleId": "image-alt",
"impact": "critical",
"description": "Images must have alt text",
"nodeCount": 5,
"wcagCriteria": ["1.1.1"]
}
]
}
],
"totalViolations": 18,
"estimatedEffort": "medium"
}/api/v1/statementsPro+List all accessibility statements for your account. Statements are ordered by last update date.
Response 200 OK
{
"statements": [
{
"id": "uuid",
"organizationName": "Acme Corp",
"websiteUrl": "https://acme.com",
"conformanceLevel": "AA",
"conformanceStatus": "partial",
"publicSlug": "acme-corp-x7k9m2",
"createdAt": "2026-02-01T00:00:00Z",
"updatedAt": "2026-02-15T10:30:00Z"
}
]
}/api/badge/{siteId}PublicReturns an SVG badge showing the current accessibility score for a site. No authentication required — designed for embedding in READMEs, dashboards, and websites.
Response 200 OKContent-Type: image/svg+xml · Cache-Control: 1 hour
Score Colors
| Score | Color |
|---|---|
| 90+ | Green |
| 70–89 | Yellow |
| 50–69 | Orange |
| <50 | Red |
| No data | Gray |
Embed Example
<img
src="https://compliascan.com/api/badge/{siteId}"
alt="CompliaScan accessibility score"
/>The siteId is the UUID returned when you create a site via POST /api/v1/sites.
Webhooks
If you provide a callback_url when creating a scan, CompliaScan will send a POST request to that URL when the scan completes. This eliminates the need for polling.
Webhook Payload
{
"event": "scan.completed",
"scanId": "scn_a1b2c3d4e5f6",
"data": {
"url": "https://example.com",
"score": 72,
"totalIssues": 18,
"criticalCount": 2,
"seriousCount": 5,
"moderateCount": 7,
"minorCount": 4
},
"detailsUrl": "https://compliascan.com/api/v1/scan/scn_a1b2c3d4e5f6"
}Your endpoint should return a 2xx status code within 10 seconds. CompliaScan will retry failed deliveries up to 3 times with exponential backoff.
Verifying Webhook Signatures
When a webhook secret is configured in your dashboard, every webhook request includes two additional headers:
X-CompliaScan-Signature— HMAC-SHA256 hex digestX-CompliaScan-Timestamp— Unix epoch in seconds when the webhook was sent
The signature is computed as HMAC-SHA256(secret, "{timestamp}.{rawBody}"). To prevent replay attacks, reject payloads where the timestamp is older than 300 seconds.
Node.js
const crypto = require('crypto');
const signature = req.headers['x-compliascan-signature'];
const timestamp = req.headers['x-compliascan-timestamp'];
// Reject stale payloads (replay protection)
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
throw new Error('Timestamp too old');
}
const message = `${timestamp}.${JSON.stringify(req.body)}`;
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(message)
.digest('hex');
if (signature !== expected) {
throw new Error('Invalid signature');
}Python
import hmac, hashlib, time
timestamp = headers['X-CompliaScan-Timestamp']
signature = headers['X-CompliaScan-Signature']
# Reject stale payloads (replay protection)
if abs(time.time() - int(timestamp)) > 300:
raise ValueError('Timestamp too old')
message = f"{timestamp}.{raw_body}"
expected = hmac.new(
secret.encode(), message.encode(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
raise ValueError('Invalid signature')Error Codes
All errors follow a consistent format with a machine-readable code and a human-readable message.
| Code | HTTP Status | Description |
|---|---|---|
AUTH_REQUIRED | 401 | No Authorization header was provided in the request. |
INVALID_KEY | 401 | The provided API key is invalid, expired, or revoked. |
RATE_LIMITED | 429 | You have exceeded the rate limit for your plan. Retry after the duration specified in the Retry-After header. |
VALIDATION | 400 | The request body failed validation. Check the details field for specific field errors. |
INVALID_URL | 400 | The provided URL is malformed or not publicly accessible. |
NOT_FOUND | 404 | The requested scan or site ID does not exist or does not belong to your account. |
PLAN_REQUIRED | 403 | This feature requires a higher plan. Check the endpoint documentation for plan requirements. |
SCAN_NOT_COMPLETE | 400 | The scan has not finished processing yet. Poll the scan status endpoint until complete. |
QUOTA_EXCEEDED | 403 | You have used all on-demand rescans for your current billing period. |
NOT_READY | 400 | The scan is still in progress. Wait for the scan to complete before accessing this resource. |
UPGRADE_REQUIRED | 403 | This feature requires a higher plan tier. Upgrade your subscription to access this endpoint. |
INVALID_CALLBACK_URL | 400 | The provided callback URL failed validation. Must be a valid, publicly accessible HTTPS URL. |
PDF_ERROR | 500 | PDF report generation failed. Retry the request or contact support if the issue persists. |
SITE_LIMIT | 403 | You have reached the maximum number of monitored sites for your plan. Remove a site or upgrade to add more. |
DUPLICATE | 409 | The resource already exists (e.g., adding a site with a domain that is already monitored). |
Error Response Format
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 32 seconds.",
"retryAfter": 32
}
}Quick Start
Get up and running in two steps. Replace cs_live_your_api_key with your actual API key from the dashboard.
1. Create a scan
curl -X POST https://compliascan.com/api/v1/scan \
-H "Authorization: Bearer cs_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"multiPage": false,
"callback_url": "https://your-server.com/webhooks/compliascan"
}'2. Check scan results
curl https://compliascan.com/api/v1/scan/scn_a1b2c3d4e5f6 \ -H "Authorization: Bearer cs_live_your_api_key"
3. Check CI/CD gate
curl "https://compliascan.com/api/v1/scan/scn_a1b2c3d4e5f6/gate?threshold=70&maxCritical=0" \ -H "Authorization: Bearer cs_live_your_api_key"
4. Generate fix suggestions
curl -X POST https://compliascan.com/api/v1/scan/scn_a1b2c3d4e5f6/fix \
-H "Authorization: Bearer cs_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{}'5. Export all scan data
curl "https://compliascan.com/api/v1/export" \ -H "Authorization: Bearer cs_live_your_api_key"
Code Examples
Quick examples for common API operations. Replace YOUR_API_KEY with your actual API key from Dashboard > Settings > API Keys.
curl
# Create a scan
curl -X POST https://compliascan.com/api/v1/scan \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "multiPage": true}'
# Get scan results (poll until status is "completed")
curl https://compliascan.com/api/v1/scan/SCAN_ID \
-H "Authorization: Bearer YOUR_API_KEY"
# CI/CD quality gate (fail if score < 80 or > 0 critical issues)
curl "https://compliascan.com/api/v1/scan/SCAN_ID/gate?threshold=80&maxCritical=0" \
-H "Authorization: Bearer YOUR_API_KEY"
# Download PDF report
curl -o report.pdf https://compliascan.com/api/v1/scan/SCAN_ID/pdf \
-H "Authorization: Bearer YOUR_API_KEY"
# List monitored sites
curl https://compliascan.com/api/v1/sites \
-H "Authorization: Bearer YOUR_API_KEY"JavaScript (Node.js / Browser)
const API_KEY = "cs_live_YOUR_API_KEY";
const BASE = "https://compliascan.com/api/v1";
// Create a scan
const { scanId } = await fetch(`${BASE}/scan`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com", multiPage: true }),
}).then((r) => r.json());
// Poll until complete
let scan;
do {
await new Promise((r) => setTimeout(r, 5000));
scan = await fetch(`${BASE}/scan/${scanId}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
}).then((r) => r.json());
} while (scan.status === "pending" || scan.status === "running");
console.log(`Score: ${scan.score}, Issues: ${scan.totalIssues}`);
// CI/CD gate check
const gate = await fetch(
`${BASE}/scan/${scanId}/gate?threshold=80&maxCritical=0`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
).then((r) => r.json());
if (!gate.passed) {
console.error("Quality gate failed:", gate.gates);
process.exit(1);
}Python
import requests, time
API_KEY = "cs_live_YOUR_API_KEY"
BASE = "https://compliascan.com/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
# Create a scan
resp = requests.post(
f"{BASE}/scan",
headers={**headers, "Content-Type": "application/json"},
json={"url": "https://example.com", "multiPage": True},
)
scan_id = resp.json()["scanId"]
# Poll until complete
while True:
scan = requests.get(f"{BASE}/scan/{scan_id}", headers=headers).json()
if scan["status"] in ("completed", "failed"):
break
time.sleep(5)
print(f"Score: {scan['score']}, Issues: {scan['totalIssues']}")
# CI/CD gate check
gate = requests.get(
f"{BASE}/scan/{scan_id}/gate",
headers=headers,
params={"threshold": 80, "maxCritical": 0},
).json()
if not gate["passed"]:
print("Quality gate FAILED:", gate["gates"])
exit(1)
print("Quality gate passed!")OpenAPI Specification
Import our OpenAPI 3.0 spec into Postman, Insomnia, or your API client of choice for interactive exploration.
Download openapi.jsonAPI Changelog
Initial Release
- 16 REST endpoints covering scans, sites, fixes, statements, and export
- Bearer token authentication with per-plan rate limiting
- CI/CD quality gate with configurable score and issue count thresholds
- Webhook delivery for scan completion events
- Multi-page scanning with plan-based page limits
- PDF report downloads (white-labeled for Business plans)
- Remediation roadmap generation
- Score history and monitoring alert configuration
- Full data export with scan results and sites (up to 500 scans)
- OpenAPI 3.0 specification available at
/api/openapi.json
Frequently Asked Questions
Which plans include API access?
What are the API rate limits?
Can I use the API in my CI/CD pipeline?
/api/v1/scan/{scanId}/gate endpoint is designed specifically for CI/CD integration. Set a minimum score threshold and maximum critical/serious issue counts. The endpoint returns a pass/fail verdict you can use to block deploys that introduce accessibility regressions.Does the API support webhook notifications?
callbackUrl when creating a scan and CompliaScan will POST the results to your URL when the scan completes. Your endpoint should return a 2xx response within 10 seconds. Failed deliveries are retried up to 3 times with exponential backoff.Ready to integrate?
API access is available on paid plans. Generate API keys from your dashboard settings and start scanning programmatically today.
Get API Access