# Site Health Audit: Accessibility, Bill 96, Cookies, llms.txt (`master_invictus/site-health-audit`) Actor

Audit a list of sites for WCAG accessibility (axe-core), Québec Bill 96 French parity, cookie pre-consent, and llms.txt AI readiness. One MasterInvictus site health score (not a certification or legal advice) per site, plus an HTML report and a PDF.

- **URL**: https://apify.com/master_invictus/site-health-audit.md
- **Developed by:** [Frank Nagy](https://apify.com/master_invictus) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Site Health Audit

Audits a list of websites and writes **one dataset item per site**. Each item has four sub-scores, the **MasterInvictus site health score (not a certification or legal advice)** from 0 to 100, and the top 10 prioritized issues.

The four checks are copied into this actor (an actor image only contains its own directory):

| Check | Copied from | What it does |
|---|---|---|
| Accessibility | `wcag-accessibility-auditor` | axe-core (MPL-2.0) on same-domain pages, WCAG A / AA / AAA |
| Bill 96 French parity | `bill96-bulk-storefront-auditor` (current main, including sitemap discovery) | French availability, prominence, product copy, policies, image alts, language switcher |
| Cookie pre-consent | `cookie-preconsent-checker` | Loads the start URL, does not click a banner, waits 5 seconds, reports cookies and known trackers |
| llms.txt / AI readiness | `llms-txt-auditor` | `/llms.txt`, `/llms-full.txt`, and AI crawler rules in robots.txt |

**Input:** `startUrls[]` (default and prefill: `https://example.com`; an empty input `{}` uses that default), `maxPagesPerSite` (default 10, max 50), and four toggles that default to on: `accessibility`, `frenchParity`, `cookies`, `llms`. Optional: `wcagLevel` (default AA), `requestTimeoutSecs` (default 20). The actor runs with 1024 MB.

Duplicate origins are audited once. The accessibility crawl and the French-parity crawl each stop at `maxPagesPerSite`. The cookie check loads the start URL only. llms.txt link HEAD checks are off so the page allowance stays tied to HTML pages; structure, the companion file, and robots rules are still scored.

**Output, one dataset item per site:**

- `score` and `scoreLabel`: **MasterInvictus site health score (not a certification or legal advice)**
- `accessibilityScore`, `frenchParityScore`, `cookieScore`, `llmsScore`, plus `subScores` (each copied score label, the cookie verdict, and any error)
- `topIssues`: at most 10 issues, highest priority first (critical axe findings, then tracking before consent, then missing French)
- `pagesFetched`, `extraPages`, `completed`
- `htmlReportKey` and `pdfReportKey`

An HTML report is saved in the key-value store for every site. A PDF of that report is saved beside it when Playwright Chromium can print it (`page.pdf()`). The run also stores `SUMMARY` and `INDEX`.

The run fails when the input is invalid or when zero sites could be fetched. A site that fails one enabled check is still in the dataset and is not billed.

### Score

Nominal weights sum to 100. Disabled checks and checks that produced no score are left out; the rest are renormalized.

| Check | Weight | Sub-score |
|---|---|---|
| Accessibility | 40 | Average axe page score. **MasterInvictus accessibility score (not a WCAG certification)** |
| Bill 96 French parity | 25 | **MasterInvictus French-parity score (not legal advice)** |
| Cookie pre-consent | 20 | **MasterInvictus cookie pre-consent score (not legal advice)**. `PASS` 100, `REVIEW_FIRST_PARTY_COOKIES` 50, `FAIL_TRACKING_BEFORE_CONSENT` 0 |
| llms.txt / AI readiness | 15 | **MasterInvictus AI readiness score (not an official standard)** |

This is not a certification and not legal advice. Automated axe checks find only part of WCAG issues. French parity is a heuristic, not an Office québécois de la langue française finding. The cookie check is not a Law 25, GDPR, or ePrivacy opinion.

### Pricing (pay-per-event)

| Event | Price |
|---|---|
| `run-started`, once per run, only after the browser launches and a page is fetched. A crash before launch is not charged. | $0.03 |
| `site-audited`, per site that completes every enabled audit, covering 10 pages | $0.04 |
| `extra-page`, per page beyond 10 on a completed site. Exactly 10 pages adds none. 11 pages adds one. | $0.004 |

Failed sites are free. Do not enable `apify-actor-start`. See [PRICING.md](PRICING.md).

### License note

Accessibility rules come from [axe-core](https://github.com/dequelabs/axe-core) by Deque Systems, licensed under **MPL-2.0**. axe-core is an unmodified npm dependency.

***

### Français / French summary

#### Audit de santé du site

Pour chaque site : accessibilité (axe-core), parité française (Loi 96), témoins et traceurs avant consentement, et préparation `llms.txt`. Le **MasterInvictus site health score (not a certification or legal advice)** va de 0 à 100. Ce n'est ni une certification ni un avis juridique. Un rapport HTML et, quand Chromium peut l'imprimer, un PDF, sont enregistrés.

***

Built and maintained by **MasterInvictus**.

# Actor input Schema

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

One site per URL. Duplicate origins are audited once. The first URL for an origin is the page the accessibility, French-parity, and cookie checks start from. When omitted, the run audits https://example.com.

## `maxPagesPerSite` (type: `integer`):

Page cap for the accessibility crawl and the French-parity crawl. site-audited includes 10 pages. Each further page on a completed site is an extra-page charge. The default of 10 does not add extra-page charges.

## `accessibility` (type: `boolean`):

Run axe-core on crawled pages. On by default.

## `frenchParity` (type: `boolean`):

Run the Bill 96 French-parity storefront check. On by default.

## `cookies` (type: `boolean`):

Load the start URL and record cookies and trackers set before consent. On by default.

## `llms` (type: `boolean`):

Check /llms.txt, /llms-full.txt, and AI crawler rules in robots.txt. On by default.

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

axe-core tag set. AA includes A.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout for fetched pages and llms.txt files.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com"
    }
  ],
  "maxPagesPerSite": 10,
  "accessibility": true,
  "frenchParity": true,
  "cookies": true,
  "llms": true,
  "wcagLevel": "AA",
  "requestTimeoutSecs": 20
}
```

# Actor output Schema

## `sites` (type: `string`):

One row per site: MasterInvictus site health score (not a certification or legal advice), four sub-scores, and the top 10 issues.

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

Sites fetched and completed, and the average MasterInvictus site health score (not a certification or legal advice).

## `reports` (type: `string`):

Key-value store keys for each site's HTML report and PDF.

# 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": [
        {
            "url": "https://example.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("master_invictus/site-health-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": [{ "url": "https://example.com" }] }

# Run the Actor and wait for it to finish
run = client.actor("master_invictus/site-health-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": [
    {
      "url": "https://example.com"
    }
  ]
}' |
apify call master_invictus/site-health-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,master_invictus/site-health-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/sgRVhh3SPiOgjBlMp/builds/2kt1r1CFv8rVfvp7Q/openapi.json
