# WCAG Accessibility Audit: axe-core Scan for EAA & ADA (`jeremydonaldsonventure/wcag-accessibility-audit`) Actor

Runs the full axe-core rule set in real Chrome on every page you list, or a whole site from its sitemap. Every WCAG 2.2 A/AA/AAA violation with the failing elements and success criteria, for the European Accessibility Act and ADA. Pay per page.

- **URL**: https://apify.com/jeremydonaldsonventure/wcag-accessibility-audit.md
- **Developed by:** [Jeremy DONALDSON](https://apify.com/jeremydonaldsonventure) (community)
- **Features:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 page audits

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does WCAG Accessibility Audit do?

**WCAG Accessibility Audit** loads every page you list, or a **whole site read from its sitemap**, in a real headless Chrome and runs the **complete axe-core rule set** on it. axe-core is the open-source accessibility engine used inside most professional testing tools. For each page you get **every WCAG violation**, the **success criterion it breaks** (for example *1.4.3 Contrast (Minimum), Level AA*), its **impact**, the **exact failing elements** (CSS selector, HTML snippet and what to fix) and a link to Deque's guide on fixing it.

It is built for the **European Accessibility Act** (EAA, applying since 28 June 2025) and **US ADA** website claims. Both point to **WCAG 2.1 Level AA**: the EAA through the European standard EN 301 549, and the ADA through the US Department of Justice's 2024 rule for state and local government websites. This Actor tests **WCAG 2.2 A and AA by default** (2.0, 2.1 and 2.2 rules), with AAA and best-practice rules as options.

You **only pay for pages that were checked**. Pages that are missing, broken, blocked by `robots.txt`, not HTML, or invalid addresses are listed free with the reason.

To try it, paste your home page into **Pages to check**, tick **Read the sitemap**, and press **Start**.

### Why use it?

- 🔎 **The full axe-core rule set, not a subset.** Lighthouse's accessibility score runs only part of axe-core. This runs every rule for the WCAG level you choose.
- 🎯 **Exact failing elements.** Each violation lists the CSS selector and HTML of the elements that fail, and the fix axe suggests. Content inside iframes is checked too.
- 📋 **WCAG success criteria on every finding**, by number and name, with the level (A, AA, AAA). Best-practice rules are labelled as "not a WCAG requirement" so they are never confused with legal requirements.
- 🧾 **"Needs review" kept apart.** Where axe can't decide (for example text over a background image), the item goes in a separate list for a person to check. It is never counted as a failure.
- 🌐 **Whole-site audits from one address.** Reads `robots.txt` and `sitemap.xml`, including sitemap indexes and `.gz` sitemaps.
- 🧭 **A site summary**: totals by impact, the worst pages, the most common violations and the WCAG criteria failed across the site.
- 📱 **Desktop or mobile.** Mobile emulates a phone screen with touch, which catches mobile-only problems such as small touch targets.
- 💸 **Failures are free**, and you can cap the cost of any run.
- 🤖 **Agent-ready.** Works as a tool in the Apify MCP server, so AI assistants can call it.
- ⏱️ Everything the Apify platform gives you: **schedules** (a monthly re-check), **API** access, **webhooks**, and exports to **JSON, CSV, Excel** or **Google Sheets**.

### Who is it for?

- **Web agencies and developers** checking client sites before launch and after changes, or prospecting with an accessibility report.
- **Online shops and service businesses selling into the EU** that now fall under the European Accessibility Act.
- **US businesses** that want to find and fix the common, machine-detectable problems that ADA website claims cite.
- **Accessibility consultants** who want a fast automated first pass across a whole site before their manual review.

### How to check a website for accessibility

1. Open the Actor and paste one or more page addresses into **Pages to check**.
2. To check the whole site, tick **Read the sitemap and check the whole site** and set **Most pages to check**.
3. Choose the **WCAG level** (AA is right for the EAA and ADA) and **Desktop** or **Mobile phone**.
4. Press **Start**. Most pages take a few seconds each; pages on the same site are loaded at most once every 2 seconds.
5. Open the **Output** tab: **Pages** for the overview, **Violations** for every problem with its elements, **Needs review** for the items a person should check, and **Site summary** for the roll-up. Download as CSV or Excel, or send it to Google Sheets.

### How much does an accessibility audit cost?

You pay **per page audit**: one page checked once.

| | Price |
|---|---|
| Page audit | **$0.005** each ($5 per 1,000), lower on Apify's Silver and Gold plans |
| Run start | Apify's standard tiny start fee |
| Failed, blocked, non-HTML or invalid addresses | **Free** |

Examples: a 50-page site is about **$0.25**. A 500-page site is about **$2.50**. Apify's free plan includes monthly credit, enough for many test audits.

You can set a **maximum cost per run** when you start it. The Actor stops cleanly before it would go over, and you keep every result it finished.

### Input example

```json
{
    "startUrls": ["https://www.abs.gov.au"],
    "crawlSitemap": true,
    "maxPages": 5,
    "wcagLevel": "AA",
    "includeBestPractices": false,
    "device": "desktop"
}
```

### Output example

One row per page (shortened). This is the W3C's deliberately inaccessible demo page, checked on mobile:

```json
{
    "url": "https://www.w3.org/WAI/demos/bad/before/home.html",
    "status": "ok",
    "pageTitle": "Welcome to CityLights! [Inaccessible Home Page]",
    "standard": "WCAG 2.2 AA + best practices",
    "issueCount": 69,
    "violatedRules": 8,
    "critical": 34,
    "serious": 12,
    "moderate": 23,
    "minor": 0,
    "failedCriteria": ["1.1.1 Non-text Content", "1.4.3 Contrast (Minimum)", "2.4.4 Link Purpose (In Context)", "2.5.8 Target Size (Minimum)", "3.1.1 Language of Page", "4.1.2 Name, Role, Value"],
    "needsReviewRules": 1,
    "needsReviewElements": 2,
    "passedRules": 23,
    "violations": [
        {
            "ruleId": "image-alt",
            "wcag": "1.1.1 Non-text Content (Level A)",
            "wcagCriteria": [{ "id": "1.1.1", "name": "Non-text Content", "level": "A" }],
            "wcagLevel": "A",
            "impact": "critical",
            "help": "Images must have alternative text",
            "helpUrl": "https://dequeuniversity.com/rules/axe/4.14/image-alt?application=axeAPI",
            "elementCount": 33,
            "elements": [
                {
                    "selector": "img[src$=\"border_left_top.gif\"]",
                    "html": "<img src=\"./img/border_left_top.gif\" width=\"10px\" height=\"10px\">",
                    "failureSummary": "Fix any of the following: Element does not have an alt attribute ..."
                }
            ]
        }
    ],
    "needsReview": [
        { "ruleId": "color-contrast", "wcag": "1.4.3 Contrast (Minimum) (Level AA)", "impact": "serious", "elementCount": 2 }
    ],
    "axeVersion": "4.14.0",
    "errorCode": null
}
```

A page that could not be checked comes back free, with the reason:

```json
{ "url": "https://www.w3.org/WAI/demos/bad/this-page-does-not-exist.html", "status": "failed", "httpStatus": 404, "errorCode": "HTTP_404", "errorMessage": "The server answered 404. Not charged." }
```

The **Site summary** (key-value store record `OUTPUT`) holds totals by impact, the five worst pages, the ten most common violations with their WCAG criteria, every criterion failed with the number of pages, and a short note on what automated testing can and can't show.

### Output fields

| Field | Meaning |
|---|---|
| `issueCount`, `critical`, `serious`, `moderate`, `minor` | Failing elements in total and by axe impact |
| `violatedRules`, `passedRules` | How many axe rules the page fails and passes |
| `failedCriteria` | WCAG success criteria with at least one violation |
| `violations` | Every violated rule, most severe first: WCAG criteria and level, impact, help text, fix link, element count and the first failing elements |
| `needsReview`, `needsReviewRules`, `needsReviewElements` | Items axe could not decide. A person should check them. Not failures |
| `standard`, `axeVersion` | What was tested and with which axe-core version |
| `status`, `errorCode`, `errorMessage` | `ok` rows are charged; `failed` and `skipped` rows are not |

### FAQ

**Is this a compliance certificate?**
No. Automated testing can check only part of WCAG. Many success criteria need a person to judge, such as whether alternative text is meaningful, whether everything works with a keyboard alone, and whether captions are accurate. Every violation this Actor reports is a real failure worth fixing, but a page with no violations has not been shown to conform. Use it to find and fix the machine-detectable problems fast, then have a person test the rest. It is not a conformance certificate, an accessibility statement or legal advice.

**What is "needs review"?**
Places where axe-core found something it could not decide automatically, for example text on a background image whose contrast it can't measure. They are listed separately so a person can check them, and are never counted as failures.

**How is this different from the accessibility score in Lighthouse or PageSpeed Insights?**
Lighthouse runs a subset of axe-core's rules and turns them into a score. This Actor runs every axe-core rule for your chosen WCAG level and lists each failing element with its WCAG criterion. For speed and SEO scores, see the Lighthouse Actor under Related tools.

**Which WCAG version does it test?**
WCAG 2.2, which includes all of 2.0 and 2.1. The EAA and the US Department of Justice rule reference WCAG 2.1 AA; WCAG 2.2 AA adds a few criteria on top (such as 2.5.8 Target Size), so a 2.2 AA run includes every axe-core rule for 2.1 AA as well.

**Can it check pages behind a login?**
No. It checks public pages only.

**Does it respect robots.txt?**
Yes. Pages a site's `robots.txt` asks bots not to load are skipped and not charged. Each site is loaded one page at a time, at most once every 2 seconds. A few sites block automated sitemap readers outright; the run then checks the addresses you gave and says so in the log.

**Is it legal?**
The Actor loads each public page once, as a browser would, and checks it. Audit sites you own, manage, or have permission to test.

**Can I run it on a schedule?**
Yes. Save your input as a task and add an Apify schedule, for example on the first of each month. Add a webhook or the Google Sheets integration to get the results where you work.

**Something went wrong.**
Open an issue on the Actor's **Issues** tab with the run link. Rows with `status: "failed"` include an error code that usually explains it: the page timed out, returned an error, or isn't a web page.

### Related tools

- [Bulk Lighthouse Site Audit](https://apify.com/jeremydonaldsonventure/bulk-lighthouse-site-audit): speed, SEO, accessibility and best-practice scores with Core Web Vitals, for a list of pages or a whole sitemap.
- [Broken Link & Sitemap Health Checker](https://apify.com/jeremydonaldsonventure/broken-link-sitemap-health-check): broken links, redirect chains, failing sitemap entries and pages missing from the sitemap.

# Actor input Schema

## `startUrls` (type: `array`):

Full page addresses, one per line, for example https://example.com/contact. https:// is added if you leave it off. With 'Read the sitemap' on, a home page is enough.

## `crawlSitemap` (type: `boolean`):

Finds more pages on the same site from its sitemap.xml (via robots.txt), up to 'Most pages to check'. Pages the site's robots.txt blocks are skipped free.

## `maxPages` (type: `integer`):

Cap on pages per run, including sitemap pages. Your run's spending limit also stops the run.

## `wcagLevel` (type: `string`):

AA is the level the European Accessibility Act (through EN 301 549) and most ADA cases use. A checks only Level A rules. AAA adds the stricter AAA rules, such as enhanced contrast. Rules from WCAG 2.0, 2.1 and 2.2 are all included.

## `includeBestPractices` (type: `boolean`):

Adds axe-core's best-practice rules, such as one main landmark and a sensible heading order. They are good advice but not WCAG requirements, and are labelled that way in the results.

## `device` (type: `string`):

Desktop is a 1366 x 900 window. Mobile emulates a phone screen (393 x 851, touch), which can show different menus and layouts.

## `maxElementsPerRule` (type: `integer`):

How many failing elements (CSS selector, HTML snippet and what to fix) to list for each broken rule on a page. The full count is always given.

## `pageTimeoutSecs` (type: `integer`):

How long to wait for a slow page to finish loading. After that, the part that has loaded is checked.

## Actor input object example

```json
{
  "startUrls": [
    "https://example.com"
  ],
  "crawlSitemap": false,
  "maxPages": 20,
  "wcagLevel": "AA",
  "includeBestPractices": false,
  "device": "desktop",
  "maxElementsPerRule": 10,
  "pageTimeoutSecs": 45
}
```

# Actor output Schema

## `results` (type: `string`):

One row per page: issue counts by impact, every WCAG violation with its success criteria and failing elements, and 'needs review' items kept apart. Rows with status 'failed' or 'skipped' were not charged.

## `summary` (type: `string`):

Totals by impact, the worst pages, the most common violations and the WCAG success criteria failed across the site, with a note on what automated testing can and can't show.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jeremydonaldsonventure/wcag-accessibility-audit").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": ["https://example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("jeremydonaldsonventure/wcag-accessibility-audit").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://example.com"
  ]
}' |
apify call jeremydonaldsonventure/wcag-accessibility-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jeremydonaldsonventure/wcag-accessibility-audit"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/cd4tkHbsEd2ABLGEU/builds/x3MfK8gLlBffTwGiZ/openapi.json
