REST API · V1

API reference

Programmatic WCAG 2.2 / ADA accessibility scanning for CI/CD pipelines, monitoring, and integrations.

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.

Base URL
https://api.adaguard.io
Version
v1 · /v1/...
POST / GET / DELETE/v1/scan{,s}Start scans, retrieve results, list history
GET/v1/scan/{id}/statusPoll scan progress
GET/v1/scan/{id}/reportDownload — json · html · csv · pdf
GET / DELETE/v1/auth-sessions{,/id}List or revoke stored authenticated sessions
GET/v1/scans/statsAccount-level usage statistics
GET/v1/badge/{domain}.svgLive score badge SVG — no API key
Plan requirement: API access requires the Professional plan or above.

Authentication

Every request must include your API key in the X-API-Key header. Generate and manage keys in Settings → API Keys.

CURL
curl "https://api.adaguard.io/v1/scans" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"
Keep your key secret. Never expose it in client-side code or public repos. Revoke compromised keys immediately from Settings.
Key format: ak_live_<32 chars>
Keys are shown once at creation — store them in GitHub Secrets or a vault.

Available scopes

ScopePermission
scans:writeStart new scans. Also satisfies scans:read checks.
scans:readRead results, poll status, list scans, download reports, list auth sessions
scans:deletePermanently delete scans and stored data
stats:readAccess 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.

BASH
#!/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 1
Non-blocking
Scan runs in background — no HTTP timeouts
Easy polling
poll_again_in tells you when to check next
Pass/Fail gate
min_score → passed field for pipeline gates

Start a Scan

POSThttps://api.adaguard.io/v1/scan
scans:writeReturns immediately

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

FieldTypeDefaultDescription
urlstring (URL)—Required. https:// only. Private IPs blocked.
scan_modeenum"single"single · crawl · sitemap · layout
max_pagesint 1–50001Max pages for crawl modes. Capped to plan limit.
min_scoreint 0–100nullCI gate. Adds passed: true/false to result.
auth_session_idstringnullReference a stored auth session.
viewportenum"desktop""desktop" or "mobile" (390×844).
exclude_pathsstring[]nullGlob patterns to skip, e.g. ["/admin", "/checkout/*", "*.pdf"]. Merged with the website’s saved exclusions.
include_subdomainsbooleantrueSet false to restrict crawl to exact hostname.

scan_mode values

singleScan only the provided URL. Fastest — ideal for CI/CD.
crawlFollow internal links up to max_pages.
sitemapDiscover URLs from sitemap.xml first, then crawl.
layoutCrawl + deduplicate structurally similar templates.
CURL
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

JSON
{
  "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

GEThttps://api.adaguard.io/v1/scan/{scan_id}/status
scans:read

Call repeatedly until status is completed or failed. Use poll_again_in as your retry interval.

CURL
curl "https://api.adaguard.io/v1/scan/SCAN_ID/status" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"

Response — completed

JSON
{
  "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

GEThttps://api.adaguard.io/v1/scan/{scan_id}
scans:read

Returns the complete result including every accessibility issue. For large scans use the report endpoint instead.

CURL
curl "https://api.adaguard.io/v1/scan/SCAN_ID" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"

Response

JSON
{
  "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

GEThttps://api.adaguard.io/v1/scans
scans:read

Returns a paginated list of completed scans. Filter by URL prefix.

CURL
curl "https://api.adaguard.io/v1/scans?limit=20&url=https://example.com" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"

Response

JSON
{
  "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

GEThttps://api.adaguard.io/v1/scans/stats
stats:read
CURL
curl "https://api.adaguard.io/v1/scans/stats" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"

Response

JSON
{"total_scans": 42, "completed_scans": 40, "scans_used": 40, "scans_limit": 200, "average_score": 78.4}

Delete a Scan

DELETEhttps://api.adaguard.io/v1/scan/{scan_id}
scans:delete
Permanent. Cannot be undone.
CURL
curl -X DELETE "https://api.adaguard.io/v1/scan/SCAN_ID" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"

Response

JSON
{"success": true, "message": "Scan deleted successfully", "scan_id": "550e8400..."}

Download Report

GEThttps://api.adaguard.io/v1/scan/{scan_id}/report?format=...
scans:read
format=json
JSON (default)
Machine-readable — ideal for CI/CD
format=html
HTML
Self-contained report page
format=csv
CSV
Issue spreadsheet sorted by severity
format=pdf
PDF
Professional report for compliance review
CURL
curl "https://api.adaguard.io/v1/scan/SCAN_ID/report?format=pdf" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE" \
  -o report.pdf

Authenticated 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
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

ValueMeaning
not_requestedNo session provided — scanned as public visitor
authenticatedSession valid — private pages accessible
expired_fallbackSession expired — fell back to public pages
public_onlySite is fully public — no auth needed

Auth Sessions

GEThttps://api.adaguard.io/v1/auth-sessions
scans:read

Returns all authenticated sessions stored on your account.

Sessions are created in the dashboard, not via API — go to Settings → Authenticated Scans and click Launch Browser. Sessions expire after 30 days.
CURL
curl "https://api.adaguard.io/v1/auth-sessions" \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"

Response

JSON
{
  "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

DELETEhttps://api.adaguard.io/v1/auth-sessions/{session_id}
scans:write

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
curl -X DELETE "https://api.adaguard.io/v1/auth-sessions/a1b2c3..." \
  -H "X-API-Key: ak_live_YOUR_KEY_HERE"

Score Badge

GEThttps://api.adaguard.io/v1/badge/{domain}.svg

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.

JSON
[![Accessibility](https://api.adaguard.io/v1/badge/adaguard.io.svg)](https://app.adaguard.io)

Errors

CodeStatusCause
401UnauthorizedMissing or invalid X-API-Key
403ForbiddenKey lacks required scope
404Not FoundScan or session not found
422Unprocessable EntityBad URL, max_pages over limit, wrong field type
429Too Many RequestsRate limit or monthly scan limit exceeded
500Server ErrorUnexpected — contact support
JAVASCRIPT
// 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.

PlanReq / hourScans / monthPages / scan
Professional10030500
Business5001501,000
EnterpriseCustomCustomCustom
HTTP
X-RateLimit-Limit:     100
X-RateLimit-Remaining: 97
X-RateLimit-Reset:     1740916800

# HTTP 429:
Retry-After: 120