WCAG Accessibility Scanner (axe-core)
Pricing
from $3.00 / 1,000 accessibility violation founds
WCAG Accessibility Scanner (axe-core)
Crawls a website with headless Chromium, runs axe-core on every page and returns one result per WCAG 2.x A/AA violation with selector, HTML, fix guidance and screenshots. Includes an A–F accessibility grade.
Pricing
from $3.00 / 1,000 accessibility violation founds
Rating
0.0
(0)
Developer
Hesling Reidinga
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Find out in minutes whether a website breaks accessibility rules — and exactly where. This Actor crawls a site with a real Chromium browser, runs axe-core (the same engine behind Lighthouse and most enterprise accessibility tools) on every page, and returns one result per offending element with the CSS selector, the HTML snippet, a plain-language explanation, a link to the fix, and a screenshot of the element. You also get an A–F accessibility grade for the site.
What it does
- Opens your start URL in headless Chromium (1280×900) and follows same-origin links — product, cart, checkout, collection, contact and content pages first — up to the number of pages you choose.
- Runs axe-core against WCAG 2.0, 2.1 and 2.2 at levels A and AA on each page. Best-practice-only rules are not included, so every finding maps to a real WCAG success criterion.
- Takes a full-page screenshot of every page and an element screenshot for the first three offending elements of each rule.
- Writes one dataset row per violation and a
SUMMARYrecord with totals, the grade and the top failing rules.
It respects robots.txt, scans one page at a time with a polite delay, and
identifies itself as AccessibilityGradeBot.
Input
| Field | Type | Default | Description |
|---|---|---|---|
startUrl | string (required) | — | The page to start from, e.g. https://example.com. |
maxPages | integer | 5 | Maximum pages to scan, including the start URL (1–50). |
includeScreenshots | boolean | true | Store page and element screenshots and link them from each result. |
{"startUrl": "https://example.com","maxPages": 5,"includeScreenshots": true}
Output
Dataset — one item per violation
Each row is one element that fails one rule. Sort or filter by impact to
prioritise, group by ruleId to plan fixes, or by pageUrl to assign work.
{"pageUrl": "https://www.w3.org/WAI/demos/bad/before/home.html","pageTitle": "Welcome to CityLights! [Inaccessible Home Page]","ruleId": "image-alt","impact": "critical","wcag": ["wcag2a", "wcag111"],"tags": ["cat.text-alternatives", "wcag2a", "wcag111", "section508", "section508.22.a", "EN-301-549", "EN-9.1.1.1", "ACT"],"help": "Images must have alternative text","helpUrl": "https://dequeuniversity.com/rules/axe/4.13/image-alt","description": "Ensure <img> elements have alternative text or a role of none or presentation","selector": "#nav > a[href$=\"tickets.html\"] > img","html": "<img src=\"./img/tickets.gif\" width=\"85\" height=\"20\">","failureSummary": "Fix any of the following:\n Element does not have an alt attribute\n ...","screenshotUrl": "https://api.apify.com/v2/key-value-stores/.../records/screenshot-p1-image-alt-1.jpg"}
impact is one of minor, moderate, serious, critical. wcag lists the
WCAG tags (wcag2a = level A, wcag2aa = level AA, wcag111 = success
criterion 1.1.1); tags keeps every axe tag, including EN 301 549 and
Section 508 references. screenshotUrl is present only when a screenshot was
taken for that element (first three elements per rule, visible elements only).
Key-value store
-
SUMMARY— the site-level result:{"grade": "F","pagesScanned": 2,"violations": 6,"nodes": 46,"byImpact": { "minor": 0, "moderate": 0, "serious": 12, "critical": 34 },"seriousNodes": 46,"topRules": [{ "ruleId": "image-alt", "nodes": 33 },{ "ruleId": "link-name", "nodes": 7 },{ "ruleId": "color-contrast", "nodes": 2 }],"targetUrl": "https://www.w3.org/WAI/demos/bad/before/home.html","platform": "custom","pages": [{ "url": "https://www.w3.org/WAI/demos/bad/before/home.html", "title": "...", "httpStatus": 200, "loadMs": 283, "violations": 46, "screenshotUrl": "..." }],"errors": [],"startedAt": "2026-09-19T21:34:10.000Z","finishedAt": "2026-09-19T21:34:18.000Z"}violationscounts distinct rule failures per page;nodescounts offending elements (the number of dataset rows).platformis a best-effort guess (shopify,woocommerce,wix,squarespace,bigcommerce,custom). -
RESULT— the complete raw scan result (all pages, all rules, all nodes). -
page-<n>.jpg— full-page screenshot of each scanned page. -
screenshot-p<n>-<rule>-<k>.jpg— element screenshots.
Grade
The grade is based on the number of serious + critical offending elements across the scanned pages:
| Grade | Serious + critical elements |
|---|---|
| A | 0 |
| B | 1–3 |
| C | 4–10 |
| D | 11–25 |
| F | more than 25 |
Use cases
- Agencies and freelancers — run it on a prospect's site before the first call and open with concrete, screenshot-backed findings; re-run after the fix to prove the improvement.
- Compliance checks (EAA, ADA, Section 508, AODA) — the European Accessibility Act applies to most consumer-facing e-commerce and services since June 2025, and ADA Title III lawsuits over websites keep rising. A monthly scan gives you a dated, repeatable record of where you stand.
- E-commerce and SaaS teams — schedule the Actor and pipe the dataset into
Slack, a spreadsheet or your issue tracker; the
selectorandhtmlfields point developers to the exact element. - Procurement and audits — get a quick, engine-based baseline before commissioning a manual audit, so the auditor's time goes to what tools can't catch.
Limits and what this is not
- Automated testing finds roughly 30–40 % of WCAG issues. Tools can check alt attributes, contrast, form labels, ARIA usage, language attributes and similar rules reliably, but they cannot judge whether alt text is meaningful, whether keyboard focus order makes sense, or whether video has accurate captions. A clean scan is a good sign, not a certificate of conformance.
- One viewport (desktop, 1280×900), no logged-in areas, no interaction with cookie walls or modals beyond what loads by default.
- Pages that need JavaScript to render are fine (real Chromium), but pages
blocked by
robots.txt, returning 4xx/5xx, or behind bot protection are skipped and listed inSUMMARY.errors. If no page at all can be scanned the run fails with an explanation. - Up to 50 pages per run; screenshots are capped at 60 per run (page screenshots are always kept, element screenshots fill the rest).
Pricing
This Actor is offered as pay per result: you pay a small fixed amount for
each violation row written to the dataset, plus nothing else — a clean site
costs you (almost) nothing to check. The exact per-result price is shown on the
Actor's Apify Store page; with the Apify free plan you can run it without a
card. If the Actor is switched to pay-per-event pricing, each dataset row is
charged as one violation event; the amount is set in the Apify Console, not
in the code.
Support
Something missing — a mobile viewport, authenticated scans, a PDF report? Open an issue on the Actor's page and describe the site type you are testing.
For developers
This Actor lives in the FullAutoBiz pnpm monorepo as apify/wcag-scanner and
wraps the workspace package @fab/scanner (Playwright + @axe-core/playwright).
Build strategy: a single esbuild bundle. The Apify platform builds the
Actor from this directory alone, without the pnpm workspace, so
scripts/build.mjs bundles src/main.ts together with @fab/scanner,
@fab/contracts, axe-core and zod into dist/main.js
(esbuild --bundle --platform=node --format=esm --external:playwright --external:apify).
Only apify and playwright stay external: playwright ships with the
apify/actor-node-playwright-chrome:22 base image (with matching browsers),
and apify is installed in the Docker build from the generated
dist/package.json. The bundle is not minified because axe-core serialises
itself with Function#toString() at runtime.
pnpm install # repo rootpnpm --filter @fab/scanner run setup # once: playwright install chromiumpnpm --filter @fab/apify-wcag-scanner build # -> dist/main.js + dist/package.jsoncd apify/wcag-scannerecho '{"startUrl":"https://www.w3.org/WAI/demos/bad/before/home.html","maxPages":2}' \> storage/key_value_stores/default/INPUT.jsonpnpm exec apify run --purge # local run; results in ./storage
dist/main.js and dist/package.json are deliberately not git-ignored in this
directory (see .gitignore) because apify push uploads the directory as-is
and .actor/Dockerfile only copies those two files. Always run build before
apify push. storage/ and apify_storage/ (local run output) are ignored.