# WCAG Accessibility Auditor — Score & Fix 30 Rules (`ryanclinton/wcag-accessibility-auditor`) Actor

Crawl any website and audit it against WCAG 2.1 accessibility standards. Checks 30 rules across four severity levels, scores each site 0–100 with letter grades A through F, and provides element-level violations with CSS selectors and fix recommendations.

- **URL**: https://apify.com/ryanclinton/wcag-accessibility-auditor.md
- **Developed by:** [Ryan Clinton](https://apify.com/ryanclinton) (community)
- **Categories:** Developer tools, AI
- **Stats:** 5 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $250.00 / 1,000 audit completeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## WCAG Accessibility Auditor

Crawl any website and audit it against WCAG 2.1 accessibility standards. Checks 30 rules across four severity levels, scores each site 0–100 with letter grades A through F, and provides element-level violations with CSS selectors and fix recommendations. Test multiple websites in a single run — no browser extensions or manual page-by-page testing required.

### Why use WCAG Accessibility Auditor?

Accessibility compliance is required by law in many jurisdictions (ADA, Section 508, EN 301 549) but testing is tedious. Browser extensions check one page at a time. Enterprise tools cost thousands per year. This actor runs in the cloud, audits multiple pages per domain, tests multiple websites in a single batch, and produces machine-readable JSON you can pipe into dashboards, Jira, or CI/CD pipelines. Get a scored audit report with actionable fix recommendations for every violation.

### Features

- **30 accessibility rules** covering images, forms, headings, ARIA, navigation landmarks, multimedia, tables, and document structure
- **Four severity levels** — critical, serious, moderate, and minor — with configurable threshold filtering
- **WCAG 2.1 Levels A, AA, and AAA** — choose the conformance level that matches your requirements
- **Multi-page crawling** — automatically discovers and audits subpages, up to 50 per domain
- **Accessibility score (0–100)** with letter grade (A through F) for quick comparison across sites
- **Actionable fix recommendations** for every violation, written in plain language
- **Element-level detail** — each violation includes the HTML snippet and CSS selector for easy developer handoff
- **Batch auditing** — test dozens of websites in a single run
- **Proxy support** — audit geo-restricted or rate-limited sites using Apify Proxy

### Use Cases

#### Compliance auditing
Audit your website against WCAG 2.1 AA (the most common legal requirement) before a compliance deadline. The severity filtering lets you prioritize critical and serious violations first.

#### Competitor benchmarking
Score competing websites side-by-side and identify accessibility advantages your competitors are missing. Higher accessibility scores improve SEO and user experience.

#### CI/CD integration
Run accessibility audits automatically after each deployment via the API. Fail the pipeline if the score drops below a threshold.

#### Agency reporting
Web agencies can audit client websites in batch and generate structured compliance reports with scores, violation counts, and prioritized fix lists.

#### Regression monitoring
Schedule weekly audits to catch accessibility regressions introduced by new code deployments. Track scores over time to ensure continuous improvement.

#### Vendor evaluation
Score third-party tools and SaaS platforms for accessibility before procurement decisions. Filter by critical violations to identify deal-breakers.

### How to Use

1. Navigate to the actor's input page on Apify Console
2. Enter one or more website URLs (e.g., `https://example.com`)
3. Adjust **Max pages per domain** to control audit depth (default: 5)
4. Select your target **WCAG conformance level** (A, AA, or AAA)
5. Set the **Minimum severity** if you only want serious or critical issues
6. Click **Start** to run the audit
7. View results in the **Dataset** tab or download as JSON, CSV, or Excel

### Input Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `urls` | string[] | Yes | — | Website URLs to audit. Bare domains auto-prefixed with `https://` |
| `maxPagesPerDomain` | integer | No | 5 | Maximum pages to crawl and audit per domain (1–50) |
| `wcagLevel` | string | No | `AA` | Target conformance level: `A` (minimum), `AA` (standard), `AAA` (enhanced) |
| `severityThreshold` | string | No | `minor` | Minimum severity: `minor` (all), `moderate`, `serious`, `critical` (critical only) |
| `proxyConfiguration` | object | No | Apify Proxy | Proxy configuration for crawling |

#### Input Examples

**Standard compliance audit:**
```json
{
  "urls": ["https://example.com"],
  "maxPagesPerDomain": 10,
  "wcagLevel": "AA",
  "severityThreshold": "minor"
}
````

**Quick critical-only check for multiple sites:**

```json
{
  "urls": ["example.com", "competitor1.com", "competitor2.com"],
  "maxPagesPerDomain": 3,
  "wcagLevel": "AA",
  "severityThreshold": "critical"
}
```

**Thorough AAA audit:**

```json
{
  "urls": ["https://example.com"],
  "maxPagesPerDomain": 50,
  "wcagLevel": "AAA",
  "severityThreshold": "minor"
}
```

#### Input Tips

- **Start with the homepage URL only.** The actor automatically discovers and crawls subpages.
- **Use Level AA** unless you have a specific reason for A or AAA. AA covers most legal requirements (ADA, Section 508, EN 301 549).
- **Increase maxPagesPerDomain for thorough audits.** Default of 5 is good for quick checks; use 20-50 before compliance deadlines.
- **Filter by severity** when fixing issues. Start with critical and serious violations for highest impact.

### Output Example

Each item in the dataset represents one audited domain:

```json
{
  "url": "https://example.com",
  "domain": "example.com",
  "score": 72,
  "grade": "C",
  "totalViolations": 14,
  "uniqueRulesViolated": 7,
  "violationsBySeverity": {
    "critical": 3,
    "serious": 4,
    "moderate": 5,
    "minor": 2
  },
  "violations": [
    {
      "ruleId": "img-alt",
      "description": "Images must have alt text",
      "severity": "critical",
      "wcagCriteria": "1.1.1",
      "wcagLevel": "A",
      "element": "<img src=\"/hero.jpg\" class=\"banner-image\">",
      "selector": "img.banner-image",
      "fix": "Add an alt attribute to all <img> elements. Use alt=\"\" for decorative images.",
      "pageUrl": "https://example.com"
    }
  ],
  "summary": {
    "critical": ["img-alt: Images must have alt text", "input-label: Form inputs must have associated labels"],
    "serious": ["heading-order: Heading levels should not skip"],
    "moderate": ["link-purpose: Links should not use generic text like 'click here'"],
    "minor": ["landmark-main: Page should have a <main> landmark"]
  },
  "pagesAudited": 5,
  "wcagLevel": "AA",
  "auditedAt": "2025-01-15T10:30:00.000Z"
}
```

### Output Fields

#### Domain-Level Fields

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | Starting URL for this domain |
| `domain` | string | Normalized domain name |
| `score` | number | Accessibility score (0–100) |
| `grade` | string | Letter grade: A (90-100), B (80-89), C (70-79), D (60-69), F (<60) |
| `totalViolations` | number | Total violation instances found across all pages |
| `uniqueRulesViolated` | number | Number of distinct accessibility rules violated |
| `violationsBySeverity` | object | Violation counts broken down by severity level |
| `summary` | object | Lists of violated rule descriptions grouped by severity |
| `pagesAudited` | number | Number of pages successfully audited |
| `wcagLevel` | string | Target conformance level used (A, AA, or AAA) |
| `auditedAt` | string | ISO 8601 timestamp of the audit |

#### Violation-Level Fields

| Field | Type | Description |
|-------|------|-------------|
| `ruleId` | string | Rule identifier (e.g., `img-alt`, `heading-order`) |
| `description` | string | Human-readable rule description |
| `severity` | string | `critical`, `serious`, `moderate`, or `minor` |
| `wcagCriteria` | string | WCAG success criterion number (e.g., `1.1.1`, `2.4.4`) |
| `wcagLevel` | string | WCAG level of this criterion (A, AA, or AAA) |
| `element` | string | HTML snippet of the violating element (truncated to 120 chars) |
| `selector` | string | CSS selector to locate the element (e.g., `img.banner-image`, `#header`) |
| `fix` | string | Plain-language fix recommendation |
| `pageUrl` | string | URL of the page where this violation was found |

### Programmatic Access (API)

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("ryanclinton/wcag-accessibility-auditor").call(run_input={
    "urls": ["https://example.com", "https://competitor.com"],
    "maxPagesPerDomain": 10,
    "wcagLevel": "AA",
    "severityThreshold": "minor",
})

for site in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"{site['domain']}: Score {site['score']}/100 ({site['grade']})")
    print(f"  Violations: {site['totalViolations']} ({site['uniqueRulesViolated']} rules)")
    for v in site["violations"]:
        if v["severity"] == "critical":
            print(f"  CRITICAL: {v['description']} on {v['pageUrl']}")
```

#### JavaScript

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

const client = new ApifyClient({ token: "YOUR_API_TOKEN" });

const run = await client.actor("ryanclinton/wcag-accessibility-auditor").call({
  urls: ["https://example.com", "https://competitor.com"],
  maxPagesPerDomain: 10,
  wcagLevel: "AA",
  severityThreshold: "minor",
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((site) => {
  console.log(`${site.domain}: ${site.score}/100 (${site.grade})`);
  console.log(`  Critical: ${site.violationsBySeverity.critical}`);
  console.log(`  Serious: ${site.violationsBySeverity.serious}`);
});
```

#### cURL

```bash
## Start the audit
curl -X POST "https://api.apify.com/v2/acts/ryanclinton~wcag-accessibility-auditor/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://example.com"],
    "maxPagesPerDomain": 10,
    "wcagLevel": "AA",
    "severityThreshold": "minor"
  }'

## Fetch results (replace DATASET_ID from the run response)
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?token=YOUR_API_TOKEN&format=json"
```

### How It Works

#### Crawling

A CheerioCrawler starts from each input URL (labeled `HOMEPAGE`), audits the page, then discovers subpages via `enqueueLinks` with same-domain filtering. Subpages are audited up to `maxPagesPerDomain`. The crawler runs with 10 concurrent workers at 120 requests/minute.

#### Rule Engine

Each page is checked against 30 rules organized by severity. Rules receive the Cheerio `$` DOM object and return an array of violations, each with the offending HTML element and a CSS selector. Rules are filtered by the target WCAG level — Level A runs 28 rules, Level AA runs all 30, Level AAA runs all 30.

#### Scoring Algorithm

The score starts at 100 and deducts points based on **unique rules violated** (not per-instance counts). This means a site with 50 images missing alt text gets the same penalty as a site with 1 — both violated the `img-alt` rule once.

| Severity | Penalty per Rule | Maximum Deduction |
|----------|-----------------|-------------------|
| Critical | -10 points | -40 (capped) |
| Serious | -5 points | -25 (capped) |
| Moderate | -3 points | -15 (capped) |
| Minor | -1 point | -10 (capped) |

Minimum possible score: **10** (all caps hit: 100 - 40 - 25 - 15 - 10).

| Grade | Score Range |
|-------|------------|
| A | 90–100 |
| B | 80–89 |
| C | 70–79 |
| D | 60–69 |
| F | Below 60 |

#### Results Aggregation

After crawling, violations from all pages are aggregated per domain. A summary groups violated rules by severity. The score and grade are calculated from the unique rules violated across all audited pages.

### Accessibility Rules Reference

#### Critical Rules (6)

| Rule ID | Description | WCAG | Fix |
|---------|-------------|------|-----|
| `img-alt` | Images must have alt text | 1.1.1 (A) | Add alt attribute to `<img>`. Use `alt=""` for decorative images |
| `input-label` | Form inputs must have associated labels | 1.3.1 (A) | Add `<label>`, `aria-label`, or `aria-labelledby` |
| `button-name` | Buttons must have discernible text | 4.1.2 (A) | Add text, `aria-label`, or `aria-labelledby` to buttons |
| `link-name` | Links must have discernible text | 2.4.4 (A) | Add text or `aria-label` to `<a>` tags |
| `document-title` | Page must have a `<title>` element | 2.4.2 (A) | Add descriptive `<title>` in `<head>` |
| `html-lang` | `<html>` must have a lang attribute | 3.1.1 (A) | Add `lang="en"` (or appropriate code) to `<html>` |

#### Serious Rules (8)

| Rule ID | Description | WCAG | Fix |
|---------|-------------|------|-----|
| `heading-order` | Heading levels should not skip | 1.3.1 (A) | Use sequential heading levels (h1 → h2 → h3) |
| `empty-heading` | Headings must not be empty | 1.3.1 (A) | Add text content or remove empty headings |
| `duplicate-id` | Element IDs must be unique | 4.1.1 (A) | Ensure all id attributes are unique |
| `meta-viewport` | Viewport must not disable user scaling | 1.4.4 (AA) | Remove `user-scalable=no`, set `maximum-scale` ≥ 2 |
| `aria-valid-attr` | ARIA attributes must be valid | 4.1.2 (A) | Use only valid ARIA attributes from WAI-ARIA spec |
| `aria-roles` | ARIA role values must be valid | 4.1.2 (A) | Use only valid role values from WAI-ARIA spec |
| `frame-title` | Frames/iframes must have a title | 2.4.1 (A) | Add title attribute to `<iframe>` elements |
| `select-name` | Select elements need accessible name | 1.3.1 (A) | Add `<label>`, `aria-label`, or title to `<select>` |

#### Moderate Rules (10)

| Rule ID | Description | WCAG | Fix |
|---------|-------------|------|-----|
| `html-lang-valid` | Lang attribute must be valid BCP 47 | 3.1.1 (A) | Use valid language codes (e.g., "en", "fr") |
| `tabindex-positive` | Avoid positive tabindex values | 2.4.3 (A) | Use `tabindex="0"` or `tabindex="-1"` only |
| `link-purpose` | Avoid generic link text ("click here") | 2.4.4 (A) | Use descriptive link text |
| `list-structure` | Lists must contain only `<li>` children | 1.3.1 (A) | Ensure `<ul>`/`<ol>` only contain `<li>` |
| `table-header` | Data tables should have `<th>` cells | 1.3.1 (A) | Add `<th>` elements and `<thead>` rows |
| `autocomplete-valid` | Autocomplete values must be valid | 1.3.5 (AA) | Use standard autocomplete values |
| `meta-refresh` | No auto-refresh via `<meta>` | 2.2.1 (A) | Remove `<meta http-equiv="refresh">` |
| `image-redundant-alt` | Alt text shouldn't duplicate surrounding text | 1.1.1 (A) | Use `alt=""` when text is redundant |
| `textarea-label` | Textareas need accessible name | 1.3.1 (A) | Add `<label>` or `aria-label` |
| `svg-img-alt` | SVGs with `role="img"` need accessible text | 1.1.1 (A) | Add `aria-label` or `<title>` to SVG |

#### Minor Rules (6)

| Rule ID | Description | WCAG | Fix |
|---------|-------------|------|-----|
| `landmark-main` | Page should have `<main>` landmark | 1.3.1 (A) | Add `<main>` or `role="main"` |
| `landmark-banner` | Page should have `<header>` landmark | 1.3.1 (A) | Add `<header>` or `role="banner"` |
| `bypass-blocks` | Page should have skip navigation link | 2.4.1 (A) | Add "Skip to main content" link |
| `video-caption` | Videos should have captions | 1.2.2 (A) | Add `<track kind="captions">` |
| `audio-caption` | Audio should have text alternatives | 1.2.1 (A) | Provide transcript or `<track>` |
| `landmark-nav` | Navigation should use `<nav>` element | 1.3.1 (A) | Wrap navigation in `<nav>` |

### How Much Does It Cost?

The actor uses minimal resources (256 MB memory, Cheerio-based parsing):

| Scenario | Sites | Pages Each | Estimated Cost | Run Time |
|----------|-------|-----------|---------------|----------|
| Quick check | 1 | 5 | ~$0.005 | ~20 seconds |
| Standard audit | 1 | 20 | ~$0.01 | ~1 minute |
| Competitor comparison | 10 | 5 each | ~$0.02 | ~1 minute |
| Thorough batch | 10 | 20 each | ~$0.05 | ~3 minutes |
| Large batch | 50 | 10 each | ~$0.10 | ~8 minutes |

### Tips

- **Start with the homepage.** The actor discovers subpages automatically.
- **Fix critical and serious first.** These have the highest impact on users with disabilities and the biggest scoring penalties.
- **Schedule regular audits** to catch regressions after code deployments.
- **Compare scores over time.** The 0–100 score makes tracking progress straightforward.
- **Use severity filtering** to focus developer attention. Show only critical issues in sprint planning; use the full report for quarterly reviews.
- **Export to Jira** via webhook or Zapier to auto-create tickets for each critical violation.

### Combine with Other Actors

| Actor | How to combine |
|-------|---------------|
| [Website Content to Markdown](https://apify.com/ryanclinton/website-content-to-markdown) | Convert website content to Markdown while auditing accessibility — parallel workflows |
| [Website Change Monitor](https://apify.com/ryanclinton/website-change-monitor) | Detect page changes, then trigger accessibility re-audit for modified pages |
| [SERP Rank Tracker](https://apify.com/ryanclinton/serp-rank-tracker) | Correlate accessibility scores with search rankings to measure SEO impact |
| [Website Tech Stack Detector](https://apify.com/ryanclinton/website-tech-stack-detector) | Identify CMS and frameworks — certain tech stacks have common accessibility patterns |
| [Shopify Store Intelligence](https://apify.com/ryanclinton/shopify-store-intelligence) | Audit Shopify stores for accessibility alongside product and theme analysis |

### Limitations

- **HTML-only analysis** — uses CheerioCrawler, not a headless browser. Cannot evaluate JavaScript-rendered content, dynamic ARIA states, or client-side interactions.
- **No color contrast checking** — requires visual rendering to compute contrast ratios, which Cheerio cannot do.
- **No keyboard navigation testing** — cannot simulate Tab key traversal or focus management.
- **~30-40% of WCAG issues** — automated tools can detect roughly a third of accessibility problems. Many issues (meaningful alt text, logical focus order, cognitive load) require human judgment.
- **No authenticated content** — only audits publicly accessible pages.
- **Rule count** — checks 30 rules, not the full WCAG 2.1 specification. Covers the most impactful and automatable criteria.

### Responsible Use

- This actor only accesses publicly visible web pages.
- Accessibility audit results should be used constructively to improve web accessibility, not to shame or penalize websites.
- Automated audits are a starting point — always follow up with manual testing and assistive technology testing for full WCAG compliance.
- See [Apify's guide on web scraping legality](https://blog.apify.com/is-web-scraping-legal/) for general guidance.

### FAQ

**What WCAG rules does this actor check?**
30 rules across four severity tiers. See the Accessibility Rules Reference section above for the complete list with WCAG criteria numbers and fix recommendations.

**How is the score calculated?**
Score starts at 100 and deducts points per unique rule violated (not per instance). Critical: -10 each (cap 40), serious: -5 (cap 25), moderate: -3 (cap 15), minor: -1 (cap 10). Minimum possible score is 10.

**Does this replace a manual accessibility audit?**
No. Automated tools catch roughly 30-40% of WCAG issues. Many problems — meaningful alt text, logical focus order, sufficient color contrast in dynamic states — require human judgment. Use this actor as a first pass, then follow up with manual and assistive technology testing.

**Can I audit sites that require authentication?**
No. The actor audits publicly accessible pages only.

**How does this compare to axe-core or Lighthouse?**
Similar rule coverage to axe-core's best-practice set. Key difference: runs in the cloud, audits multiple sites in batch, and produces structured JSON for integration. Lighthouse also includes performance metrics this actor doesn't cover.

**What WCAG level should I use?**
Level AA for most cases. It covers the requirements of ADA, Section 508, EN 301 549, and most accessibility laws. Level A is the bare minimum. Level AAA is enhanced and rarely required by law.

### Integrations

- [Slack](https://apify.com/integrations/slack) — get notified when audits find critical violations or scores drop
- [Google Sheets](https://apify.com/integrations/google-sheets) — export results for tracking and reporting
- [Zapier](https://apify.com/integrations/zapier) — create Jira tickets, send Slack alerts, or trigger workflows on completion
- [Make](https://apify.com/integrations/make) — connect audit results to multi-step automation workflows
- [Apify API](https://docs.apify.com/api/v2) — trigger audits from CI/CD pipelines or custom applications
- [Webhooks](https://docs.apify.com/platform/integrations/webhooks) — POST results to your endpoint on completion
- [GitHub Actions](https://docs.apify.com/platform/integrations) — run audits as part of deployment pipelines

# Actor input Schema

## `urls` (type: `array`):

List of website URLs to audit for accessibility (e.g., https://example.com).

## `maxPagesPerDomain` (type: `integer`):

Maximum pages to crawl per domain. More pages = more thorough audit. Pages with forms are prioritized.

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

Target WCAG 2.1 conformance level. AA is the most common legal requirement.

## `severityThreshold` (type: `string`):

Only include violations at or above this severity level in the output.

## `proxyConfiguration` (type: `object`):

Proxy settings for crawling.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://example.com"
  ],
  "maxPagesPerDomain": 5,
  "wcagLevel": "AA",
  "severityThreshold": "minor",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "urls": [
        "https://apify.com",
        "https://example.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ryanclinton/wcag-accessibility-auditor").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 = {
    "urls": [
        "https://apify.com",
        "https://example.com",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

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

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

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

```

## CLI example

```bash
echo '{
  "urls": [
    "https://apify.com",
    "https://example.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call ryanclinton/wcag-accessibility-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ryanclinton/wcag-accessibility-auditor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "WCAG Accessibility Auditor — Score & Fix 30 Rules",
        "description": "Crawl any website and audit it against WCAG 2.1 accessibility standards. Checks 30 rules across four severity levels, scores each site 0–100 with letter grades A through F, and provides element-level violations with CSS selectors and fix recommendations.",
        "version": "1.0",
        "x-build-id": "iuicgNRpPxQZM9Jot"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ryanclinton~wcag-accessibility-auditor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ryanclinton-wcag-accessibility-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ryanclinton~wcag-accessibility-auditor/runs": {
            "post": {
                "operationId": "runs-sync-ryanclinton-wcag-accessibility-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ryanclinton~wcag-accessibility-auditor/run-sync": {
            "post": {
                "operationId": "run-sync-ryanclinton-wcag-accessibility-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "urls"
                ],
                "properties": {
                    "urls": {
                        "title": "Website URLs",
                        "type": "array",
                        "description": "List of website URLs to audit for accessibility (e.g., https://example.com).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxPagesPerDomain": {
                        "title": "Max pages per domain",
                        "minimum": 1,
                        "maximum": 50,
                        "type": "integer",
                        "description": "Maximum pages to crawl per domain. More pages = more thorough audit. Pages with forms are prioritized.",
                        "default": 5
                    },
                    "wcagLevel": {
                        "title": "WCAG conformance level",
                        "enum": [
                            "A",
                            "AA",
                            "AAA"
                        ],
                        "type": "string",
                        "description": "Target WCAG 2.1 conformance level. AA is the most common legal requirement.",
                        "default": "AA"
                    },
                    "severityThreshold": {
                        "title": "Minimum severity to report",
                        "enum": [
                            "minor",
                            "moderate",
                            "serious",
                            "critical"
                        ],
                        "type": "string",
                        "description": "Only include violations at or above this severity level in the output.",
                        "default": "minor"
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings for crawling."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
