REST API · v1
API Documentation
AccessPulse exposes a single REST endpoint. Send a URL, get back a WCAG 2.2 AA accessibility report with a score, violations, and per-element details. No SDK required.
https://accesspulse.devQuick start
Run a scan with a single curl command — no API key required for free scans:
curl -s -X POST https://accesspulse.dev/api/scan \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}' | jq .With an API key (paid plans — higher rate limits):
curl -s -X POST https://accesspulse.dev/api/scan \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"url": "https://example.com"}' | jq .POST /api/scan
Scans a URL for WCAG 2.2 AA violations. Synchronous — response returns the complete report.
Request headers
| Header | Required | Value |
|---|---|---|
| Content-Type | Yes | application/json |
| Authorization | No | Bearer YOUR_API_KEY |
Request body
{
"url": "https://example.com" // required — must be http:// or https://
}| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Public URL to scan. Must be reachable from the internet. Private IPs and internal hostnames are blocked. |
Response format
HTTP 200 OK on success. The scan runs synchronously — the connection stays open until the scan completes (typically 10–30 seconds).
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"url": "https://example.com",
"score": 78,
"violations_count": 2,
"passes_count": 14,
"incomplete_count": 0,
"violations": [
{
"id": "landmark-one-main",
"impact": "moderate",
"description": "Ensure the document has a main landmark",
"help": "Document should have one main landmark",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.10/landmark-one-main",
"tags": ["cat.semantics", "best-practice"],
"nodes_count": 1,
"nodes": [
{
"html": "<html lang=\"en\">",
"target": ["html"],
"failureSummary": "Fix all of the following:\n Document does not have a main landmark"
}
]
}
]
}Fields
| Field | Type | Description |
|---|---|---|
| id | string | UUID for this scan. Use with GET /api/scan/{id} to retrieve later. |
| url | string | The URL that was scanned (normalized). |
| score | integer | Accessibility score 0–100. Computed as a weighted ratio of passes to total checks. Critical violations are weighted 10×, serious 5×, moderate 2×, minor 1×. |
| violations_count | integer | Total number of distinct WCAG rules that failed. |
| passes_count | integer | Total number of distinct WCAG rules that passed. |
| incomplete_count | integer | Rules that could not be fully determined automatically (require manual review). |
| violations[].id | string | axe-core rule identifier (e.g. color-contrast, image-alt). |
| violations[].impact | string | Severity: critical · serious · moderate · minor |
| violations[].nodes_count | integer | Number of DOM elements affected by this rule. |
| violations[].nodes | array | Up to 10 affected elements with HTML snippet, CSS selector, and remediation summary. |
Error codes
All errors return a JSON body with a single error field.
| Status | Meaning | Common cause |
|---|---|---|
| 400 | Bad request | Missing or invalid url field. Private IPs, localhost, and non-HTTP URLs are rejected. |
| 401 | Unauthorized | Invalid or missing API key when accessing authenticated endpoints. |
| 403 | Forbidden | Subscription is inactive (payment failed). Update your payment method. |
| 429 | Rate limit exceeded | Anonymous: 10 scans/hour per IP. Paid plans: per-subscription monthly quota. |
| 500 | Internal error | Unexpected server error. Transient — retry after a short delay. |
| 502 | Scan failed | The target URL was unreachable, returned an error, or the page failed to load. Check the URL is publicly accessible. |
Rate limits
| Plan | Scans / month | Notes |
|---|---|---|
| Free (no key) | 25 | Per IP address. Also limited to 10 scans/hour per IP. |
| Developer — $29/mo | 500 | Identified by API key. |
| Team — $149/mo | 2,000 | Identified by API key. |
| Agency — $399/mo | 10,000 | Identified by API key. |
Quotas reset on the first of each month. Upgrade your plan →
GitHub Action
Block PRs that regress your accessibility score. Zero dependencies — uses only Node.js built-ins.
Install
name: Accessibility
on:
pull_request:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: WCAG 2.2 scan
uses: accesspulse/scan@v1
with:
url: ${{ vars.SITE_URL }} # e.g. https://your-preview-url.vercel.app
threshold: 80 # fail if score drops below 80
api-key: ${{ secrets.ACCESSPULSE_API_KEY }} # optionalInputs
| Input | Required | Default | Description |
|---|---|---|---|
| url | Yes | — | URL to scan. |
| threshold | No | 80 | Minimum score (0–100). Step fails if score is below this. |
| api-key | No | — | AccessPulse API key for paid-tier limits. Omit for free scans. |
Outputs
| Output | Description |
|---|---|
| score | Accessibility score (0–100). |
| violations | Number of WCAG violations found. |
| passed | 'true' if score ≥ threshold, 'false' otherwise. |
Use outputs in a later step
- name: WCAG 2.2 scan
id: a11y
uses: accesspulse/scan@v1
with:
url: https://example.com
threshold: 80
- name: Post score to PR
run: echo "Score: ${{ steps.a11y.outputs.score }}/100"Scan a Vercel preview URL
name: Accessibility
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Wait for Vercel preview
uses: patrickedqvist/wait-for-vercel-preview@v1.3.1
id: vercel
with:
token: ${{ secrets.GITHUB_TOKEN }}
max_timeout: 120
- name: WCAG 2.2 scan
uses: accesspulse/scan@v1
with:
url: ${{ steps.vercel.outputs.url }}
threshold: 80
api-key: ${{ secrets.ACCESSPULSE_API_KEY }}Built on axe-core (MPL-2.0) by Deque Systems. AccessPulse is not affiliated with Deque. Questions? Contact us →