# Accessibility Regression Monitor — Branded HTML & PDF (`zina1989/accessibility-regression-monitor`) Actor

Track accessibility changes after website updates. Download customer-branded HTML and tagged PDF reports. Free beta; Apify platform usage applies.

- **URL**: https://apify.com/zina1989/accessibility-regression-monitor.md
- **Developed by:** [Zina](https://apify.com/zina1989) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

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

### Show clients what changed after a website update

For **small web agencies maintaining client websites**: compare accessibility scans before and after a release, review newly detected issues, and download a report with your client's business name and branding.

Use it alongside your maintenance or release checks when you need to explain **what changed and what needs attention**. An unsuccessful scan preserves previous findings instead of treating the page as fixed.

### See the report before running a scan

[![Sample report for fictional agency Northstar Digital: summary, coverage and review priorities]({{sample:cover-preview.png}})]({{sample:release-review.pdf}})

**[Open sample HTML]({{sample:release-review.html}}) · [Download sample PDF]({{sample:release-review.pdf}}) · [See the follow-up PDF]({{sample:after-remediation.pdf}})**

These reports use six pages we control and a fictional business, Northstar Digital. They demonstrate the workflow; they are not customer results or a compliance certificate.

### A complete before-and-after example

| Step | What happens | What the agency sees |
|---|---|---|
| Before a release | Scan the six owned demo pages to establish history. | Initial observations form a baseline. |
| After the release | Introduce three deliberate issues, retain one existing issue, fix another and make one page incomplete. | **3 New**, **1 Still detected**, **1 No longer detected**, **1 Could not check**. The incomplete page keeps its previous baseline. |
| After remediation | Restore the three deliberate regressions and rescan the incomplete page. | The follow-up report shows the updated findings and restored coverage. |

[Compare the release review]({{sample:release-review.html}}) with [the follow-up report]({{sample:after-remediation.html}}). The report includes selectors, element evidence and suggested next actions for a developer to review.

The interactive Store demo below uses **one button**, so it is quicker than the six-page downloadable example.

**Free beta:** there is no developer fee or subscription for this Actor. Apify platform usage, storage and transfer still consume your Apify credits or incur charges under your own plan. No paid AI API or residential proxy is required. Future pricing is not promised; any change would follow Apify's pricing rules.

### Try the demo

Keep the prefilled example input and run the Actor. The demo uses an owned in-memory page and does not fetch the placeholder URL. Open **HTML report**, **PDF report** or **Results** in the run's Output tab.

For a complete comparison, keep the same monitor name throughout:

1. Run **Try demo named** to establish the baseline.
2. Switch to **Try demo unnamed** and run again. The button's missing accessible name appears as **New**.
3. Switch back to **Try demo named** and run again. The finding is **No longer detected**.

Use a separate monitor name when you move to a real website. A repeat scan with the same input will not deliberately introduce an issue.

### What does a run cost?

There is **no developer fee during the free beta**. Your Apify account still covers platform usage.

| Measured release test, 28 October 2026 | Pages | Recorded platform usage |
|---|---|---|
| Owned button demo, branded HTML and PDF | 1 | About **$0.0011** |
| Public sandbox page, branded HTML and PDF | 1 | About **$0.0019** |

These are two individual successful runs of build **0.1.4**, at **1,024 MB** memory. Values are the API's `usageTotalUsd` recorded during validation, not guaranteed prices or an invoice forecast. Large pages, timeouts, failures, storage retention and downloads can change usage. Do not multiply these examples into a guaranteed whole-site budget. Review your own run's Usage tab and account limits. The samples exclude development builds and future storage/download activity.

### Use your own Apify account

Runs, reports and comparison history belong to the Apify account that starts the Actor. Platform usage draws on that account's credits or plan; the developer does not share credentials or pay for customer runs. There is no additional frontend login or API token required for the copy-and-paste input workflow.

### Scan your website

1. Set **Scan mode** to **Scan my website pages**.
2. Enter 1–100 exact public HTTPS page URLs and confirm you are authorised to test them.
3. Choose a unique **Monitor name** and keep it unchanged for future comparisons. Use a different name per client.
4. Enter the customer's business name and optional branding settings.
5. Run the scan and download its HTML/PDF reports. Run it again after your website update. Check the terminal status message and **Assessment coverage and run summary**: they report how many pages were assessed. If no pages can be assessed, the Actor fails after saving its diagnostic reports. Partial coverage is explicitly labelled even when the run completes.

The first successful scan establishes a baseline. Existing findings on that run are not new regressions. A successful repeat scan compares against the previous successful scan, not against historic data from before your first run.

For dynamic pages, optionally map each URL to a short piece of expected page text. Missing expected text, redirects, visible consent dialogs, blocked resources or challenge pages can produce an incomplete scan. Supply the exact final URL. The Actor does not log in. It attempts the configured consent choice on supported banners, including supported Usercentrics controls in open shadow roots. Unsupported consent choices and paywalls require manual review.

### Business-branded reports

Start with just **Business name**; a logo is optional. The default colours and layout already produce a branded report, so you can leave the advanced fields unchanged for your first scan.

Customise report title, project/release name, primary and accent colours, optional agency credit, footer text and an embedded PNG logo. Primary colours must be readable against white text. Leave **Prepared by** blank for a report without agency credit. There is no personal author name or mandatory product branding in the report.

Logos must be PNG data URIs (`data:image/png;base64,...`), under 180 KB encoded and no larger than 2048 × 2048 pixels. Remote logo URLs and SVG are not supported.

### Outputs

| Output | Contents |
|---|---|
| HTML report | Executive summary, page coverage, finding register, element evidence and contextual guidance. Section and finding links support keyboard navigation. |
| PDF report | Business-branded A4 export with tags, English language metadata, heading bookmarks and internal finding links. Optional; enabled by default. |
| Results | JSON/CSV-compatible event rows with page URL, change type, Axe rule, impact, selector and evidence where available. |
| Summary | Assessment status (`complete`, `partial`, `none`), assessed/requested pages, coverage percentage, scan errors, resource-warning count, elapsed time and demo flag. |

`baseline` and `rebaseline` are initial observations. `resolved` means a finding was absent in the latest scan, not independent confirmation of a fix. `needs_review` includes inconclusive rules requiring manual review, even when no confirmed violations were found. `scan_error` means current findings are unknown. `page_summary` gives per-page coverage.

Counts use different units: confirmed findings and manual-review entries are rule-and-element records, not necessarily unique DOM elements; an inconclusive rule-page result can cover several elements on one page. The register contains finding, manual-review, resolved and incomplete-page entries. The dataset additionally includes one summary record per assessed page. The HTML/PDF explains this arithmetic, and the Summary exposes the same named counts. Pages sharing a filename receive distinct path-based labels such as **Before / Home** and **After / Home**.

### Schedule repeat checks

Save a task using the same monitor name and schedule it in Apify. Avoid overlapping schedules; if another run already holds the monitor lock, the later run exits without writing findings. Use separate monitor names for separate clients.

### Scope and limitations

- Automated Axe-core checks tagged WCAG 2.1 A/AA, in Chromium's initial desktop page state.
- Only the supplied URLs are scanned; no whole-site crawl or authenticated journeys.
- Public HTTPS only. No private networks, credentials in URLs, custom ports, CAPTCHA bypass or login automation.
- Robots policy is checked for the target page. A missing policy (404) permits scanning; unavailable, redirected or disallowing policies cause a scan error.
- Resource/readiness checks are conservative. Unknown failed scripts, styles, fonts or images may prevent comparison even when the page appears usable to a person. Explicitly recognized nonessential analytics scripts (selected tracking hosts and canonical `/analytics/piwik/piwik.js` or `/analytics/matomo/matomo.js` paths) produce warnings while checks continue. Warnings are visible in page coverage, datasets and diagnostics; unknown application scripts and policy/budget failures remain blocking. Public HTTPS subframes are validated separately from main-page redirects.
- Rule-and-selector identity can produce a resolved/new pair after DOM changes. A major page redesign may require a fresh monitor name.
- Per page: up to 90 seconds, 500 routed requests by default (configurable 50 to 1,000), 200 proxy connections, 25 MB transferred, and 30,000 DOM elements before Axe. The scan loop allows 100 seconds per submitted page plus 120 seconds overhead. A 100-page scan can take over two hours; allow at least three hours in the run timeout. Browser/PDF export adds overhead.
- PDF tagging, links and table structures were inspected, and the demo HTML passed automated and keyboard checks. **Neither PDF/UA conformance nor manual screen-reader compatibility has been certified.**
- Automated testing does not establish full WCAG compliance or legal compliance. Manual keyboard, screen-reader and user-journey testing remains necessary.
- This is a beta. Cancellation recovery, exactly-once output and long-running reliability have limits. A failed run may leave partial outputs.

### Data handling

The Actor visits the pages you supply and their page resources, and saves findings and comparison snapshots in your Apify storage. HTML snippets and page-identity tokens can contain website content. Use authorised non-sensitive pages, and manage storage access and retention in your Apify account. No external AI model, analytics service or third-party logo host receives the report.

Named history stores use `a11y-<monitorName>`. Lease queues use `a11y-lock-<monitorName>`. Removing the history resets comparisons. Do not share a monitor name between unrelated websites.

### Feedback and support

Use the Actor's Issues tab for feedback. Include the run ID and a non-sensitive reproduction example. Never post API tokens, private credentials, personal customer data or confidential reports. Helpful feedback: whether the scan completed, whether the change classification was useful, and which report detail was missing.

Independent tool using Playwright and the open-source Axe-core engine. No affiliation with or endorsement by Microsoft or Deque Systems is implied.

# Actor input Schema

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

For real scans, supply 1–100 exact public HTTPS URLs you are authorised to test. Scans run sequentially; large batches can take over two hours. The demo ignores this placeholder.

## `monitorName` (type: `string`):

Reuse this name for comparison history. Lowercase letters, digits and hyphens; at most 60 characters.

## `authorized` (type: `boolean`):

Confirm permission before scanning real websites. The prefilled example scans only an owned in-memory demo page.

## `expectedTexts` (type: `object`):

Optional mapping from each supplied URL to a short text that must appear. Recommended to detect unexpected login or error pages.

## `demoVariant` (type: `string`):

Choose a demo for a safe first run. Select Scan my website pages before testing your own URLs.

## `consentMode` (type: `string`):

Try supported reject-optional controls, leave banners untouched, or explicitly accept cookies. Does not bypass paywalls. The action is recorded in scan results.

## `consentSelectors` (type: `object`):

Optional exact URL to Playwright selector mapping. Used only when consent mode is not off; selecting a button explicitly authorises that click.

## `captureEvidence` (type: `boolean`):

Save viewport screenshots and resource-failure details. Screenshots can contain page content; use non-sensitive pages.

## `maxRequests` (type: `integer`):

Bounded loading allowance for complex pages. The time, connection and byte limits still apply.

## `businessName` (type: `string`):

Customer business shown in the header and Prepared for section. Maximum 100 characters.

## `reportTitle` (type: `string`):

Custom cover and document title. Maximum 120 characters.

## `engagementName` (type: `string`):

For example: Website redesign — release 2.4. Maximum 140 characters.

## `preparedBy` (type: `string`):

Optional agency or team credit. Leave blank to omit. Maximum 100 characters.

## `footerText` (type: `string`):

Custom confidentiality or team text. Maximum 180 characters.

## `primaryColor` (type: `string`):

Six-digit hex colour, e.g. #163c36. Must contrast with white text at least 4.5:1.

## `accentColor` (type: `string`):

Six-digit hex colour for decorative borders, e.g. #b7792d.

## `logoDataUri` (type: `string`):

PNG as a data:image/png;base64,... URI. Maximum 180 KB encoded and 2048 by 2048 pixels. No remote logo URLs.

## `exportPdf` (type: `boolean`):

Generate a PDF using the same business branding as the HTML report. Adds a browser export step.

## Actor input object example

```json
{
  "urls": [
    "https://owned-fixture.invalid/"
  ],
  "monitorName": "my-website",
  "authorized": true,
  "expectedTexts": {},
  "demoVariant": "named",
  "consentMode": "reject",
  "consentSelectors": {},
  "captureEvidence": true,
  "maxRequests": 500,
  "exportPdf": true
}
```

# Actor output Schema

## `htmlReport` (type: `string`):

Customer-branded report with coverage, finding links and suggested review actions.

## `pdfReport` (type: `string`):

Tagged PDF with bookmarks, available when Generate PDF is enabled.

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

Finding events and page summaries.

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

Assessment status (complete, partial or none), assessed/requested pages, coverage percentage, incomplete pages, resource warnings and runtime. Run completion alone does not establish assessment coverage.

# 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://owned-fixture.invalid/"
    ],
    "authorized": true,
    "demoVariant": "named"
};

// Run the Actor and wait for it to finish
const run = await client.actor("zina1989/accessibility-regression-monitor").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://owned-fixture.invalid/"],
    "authorized": True,
    "demoVariant": "named",
}

# Run the Actor and wait for it to finish
run = client.actor("zina1989/accessibility-regression-monitor").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 '{
  "urls": [
    "https://owned-fixture.invalid/"
  ],
  "authorized": true,
  "demoVariant": "named"
}' |
apify call zina1989/accessibility-regression-monitor --silent --output-dataset

```

## MCP server setup

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

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/ljpBoSihjNOeZKPMB/builds/LO1sYmmiXfpv1SItP/openapi.json
