API reference
Introduction
The ADAGuard Public API v1 lets you run WCAG 2.2 / ADA accessibility scans programmatically and retrieve structured results. Common use cases: automated CI/CD quality gates, continuous site monitoring, and embedding accessibility checks into your CMS or deployment workflow.
https://api.adaguard.iov1 · /v1/...Authentication
Every request must include your API key in the X-API-Key header. Generate and manage keys in Settings → API Keys.
curl "https://api.adaguard.io/v1/scans" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"ak_live_<32 chars>Keys are shown once at creation — store them in GitHub Secrets or a vault.
Available scopes
| Scope | Permission |
|---|---|
| scans:write | Start new scans. Also satisfies scans:read checks. |
| scans:read | Read results, poll status, list scans, download reports, list auth sessions |
| scans:delete | Permanently delete scans and stored data |
| stats:read | Access account-level usage statistics |
Quick Start — CI/CD in 60 seconds
The API is async-first: POST /scan returns immediately with a scan_id. Poll /status until done, then use min_score as an automatic pass/fail gate.
#!/bin/bash
API_KEY="ak_live_YOUR_KEY_HERE"
BASE="https://api.adaguard.io"
# 1. Start scan
RESP=$(curl -s -X POST "$BASE/v1/scan" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "scan_mode": "single", "min_score": 80}')
SCAN_ID=$(echo $RESP | jq -r '.scan_id')
# 2. Poll until completed
while true; do
STATUS=$(curl -s "$BASE/v1/scan/$SCAN_ID/status" -H "X-API-Key: $API_KEY")
STATE=$(echo $STATUS | jq -r '.status')
[ "$STATE" = "completed" ] || [ "$STATE" = "failed" ] && break
sleep $(echo $STATUS | jq -r '.poll_again_in // 5')
done
# 3. CI gate
PASSED=$(echo $STATUS | jq -r '.passed')
[ "$PASSED" = "false" ] && echo "Score below threshold — failing build" && exit 1Start a Scan
Queues a new scan and returns a scan_id immediately. The scan runs in the background.
Add an Idempotency-Key header to retry safely: the same key within 24 hours returns the scan it already started instead of starting another.
Request body
| Field | Type | Default | Description |
|---|---|---|---|
| url | string (URL) | — | Required. https:// only. Private IPs blocked. |
| scan_mode | enum | "single" | single · crawl · sitemap · layout |
| max_pages | int 1–5000 | 1 | Max pages for crawl modes. Capped to plan limit. |
| min_score | int 0–100 | null | CI gate. Adds passed: true/false to result. |
| auth_session_id | string | null | Reference a stored auth session. |
| viewport | enum | "desktop" | "desktop" or "mobile" (390×844). |
| exclude_paths | string[] | null | Glob patterns to skip, e.g. ["/admin", "/checkout/*", "*.pdf"]. Merged with the website’s saved exclusions. |
| include_subdomains | boolean | true | Set false to restrict crawl to exact hostname. |
scan_mode values
curl -X POST "https://api.adaguard.io/v1/scan" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"scan_mode": "crawl",
"max_pages": 25,
"min_score": 80
}'Response
{
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"url": "https://example.com",
"created_at": "2026-03-02T10:00:00Z",
"poll_url": "/v1/scan/550e8400.../status"
}Poll Scan Status
Call repeatedly until status is completed or failed. Use poll_again_in as your retry interval.
curl "https://api.adaguard.io/v1/scan/SCAN_ID/status" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"Response — completed
{
"scan_id": "550e8400...",
"status": "completed",
"score": 74,
"passed": false,
"pages_scanned": 25,
"issues_critical": 8,
"issues_warning": 21,
"issues_info": 12,
"poll_again_in": null
}Get Full Scan Result
Returns the complete result including every accessibility issue. For large scans use the report endpoint instead.
curl "https://api.adaguard.io/v1/scan/SCAN_ID" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"Response
{
"id": "550e8400...",
"status": "completed",
"score": 74,
"pages_scanned": 25,
"elements_checked": 12840,
"issues_critical": 8,
"issues": [
{
"severity": "critical",
"type": "alt-text-missing",
"description": "Image is missing alt text",
"wcag": ["1.1.1"],
"help_url": "https://www.w3.org/WAI/WCAG22/quickref/#non-text-content",
"elements": [{"html": "<img src='hero.jpg'>", "selector": "main > img"}]
}
]
}List Scans
Returns a paginated list of completed scans. Filter by URL prefix.
curl "https://api.adaguard.io/v1/scans?limit=20&url=https://example.com" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"Response
{
"scans": [
{"id": "550e8400...", "url": "https://example.com", "score": 74, "status": "completed", "pages_scanned": 25}
],
"total": 42, "limit": 20, "offset": 0, "has_more": true
}Scan Statistics
curl "https://api.adaguard.io/v1/scans/stats" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"Response
{"total_scans": 42, "completed_scans": 40, "scans_used": 40, "scans_limit": 200, "average_score": 78.4}Delete a Scan
curl -X DELETE "https://api.adaguard.io/v1/scan/SCAN_ID" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"Response
{"success": true, "message": "Scan deleted successfully", "scan_id": "550e8400..."}Download Report
curl "https://api.adaguard.io/v1/scan/SCAN_ID/report?format=pdf" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE" \
-o report.pdfAuthenticated Scanning
To scan login-protected pages, create a session in Settings → Authenticated Scans and pass the session ID in your request. Raw credentials are never accepted through the API.
curl -X POST "https://api.adaguard.io/v1/scan" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"url": "https://app.example.com/dashboard", "scan_mode": "crawl", "auth_session_id": "sess_abc123"}'auth_status values
| Value | Meaning |
|---|---|
| not_requested | No session provided — scanned as public visitor |
| authenticated | Session valid — private pages accessible |
| expired_fallback | Session expired — fell back to public pages |
| public_only | Site is fully public — no auth needed |
Auth Sessions
Returns all authenticated sessions stored on your account.
curl "https://api.adaguard.io/v1/auth-sessions" \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"Response
{
"auth_sessions": [
{"session_id": "a1b2c3...", "domain": "app.example.com", "status": "active", "expires_at": "2026-03-17T09:30:00Z"}
],
"total": 1,
"note": "Use session_id as auth_session_id in POST /v1/scan."
}Revoke a session
Deletes a stored session immediately, so it can no longer start authenticated scans — the API equivalent of deleting it in the dashboard, and the kill-switch to reach for if a session may have been compromised. Pass the session_id from the list above. Returns 404 if no such session exists on your account.
curl -X DELETE "https://api.adaguard.io/v1/auth-sessions/a1b2c3..." \
-H "X-API-Key: ak_live_YOUR_KEY_HERE"Score Badge
An SVG badge showing the most recent completed score for a domain. No API key required — it is a public image, so it renders in a README or on any page. Cached for 5 minutes.
[](https://app.adaguard.io)Errors
| Code | Status | Cause |
|---|---|---|
| 401 | Unauthorized | Missing or invalid X-API-Key |
| 403 | Forbidden | Key lacks required scope |
| 404 | Not Found | Scan or session not found |
| 422 | Unprocessable Entity | Bad URL, max_pages over limit, wrong field type |
| 429 | Too Many Requests | Rate limit or monthly scan limit exceeded |
| 500 | Server Error | Unexpected — contact support |
// 422 — validation error
{"error": "Invalid request", "details": [{"field": "url", "message": "Cannot scan private addresses"}]}
// 429 — monthly limit
{"detail": {"error": "Monthly scan limit reached", "scans_used": 200, "scans_limit": 200, "upgrade_url": "/billing"}}Rate Limits
Per-key sliding 1-hour window. Limit headers on every response.
| Plan | Req / hour | Scans / month | Pages / scan |
|---|---|---|---|
| Professional | 100 | 30 | 500 |
| Business | 500 | 150 | 1,000 |
| Enterprise | Custom | Custom | Custom |
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1740916800
# HTTP 429:
Retry-After: 120