# Broken Link Checker & SEO Site Audit: WCAG, Redirects, SSL (`jordydev/site-qa-crawler`) Actor

Crawl your website to find broken links, redirect chains and loops, missing SEO basics (title, meta description, H1, canonical, Open Graph, hreflang), WCAG accessibility issues (axe-core) and SSL expiry. Get a prioritised fix list, CSV and client-ready PDF report. Respects robots.txt.

- **URL**: https://apify.com/jordydev/site-qa-crawler.md
- **Developed by:** [Jordy](https://apify.com/jordydev) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 page crawls

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

## Broken Link Checker & SEO Site Audit: Redirects, WCAG Accessibility & SSL

**One polite crawl of your website finds broken links, redirect chains, missing SEO basics, WCAG accessibility issues and expiring SSL certificates, and turns them into one prioritised fix list, a CSV and a client-ready PDF, from $4 per 1,000 pages.**

Enter a start URL and the crawler checks every page and link it is allowed to, then groups all findings by type, ordered by severity and number of affected pages, with example URLs and how to fix them. A lightweight Screaming Frog alternative that runs in the cloud, on a schedule, with pay-per-page pricing.

### What can it do?

- ✅ **Broken link checker**: every internal link (and external links, optional) with HTTP status, error and anchor text, reported on the page that carries it. Links are kept exactly as written, so there are no invented broken links.
- ✅ **Redirect checker**: the real hop-by-hop chain with status codes; loops, chains of 2+ hops and HTTPS → HTTP downgrades are flagged separately.
- ✅ **SEO basics**: title and meta description (presence and length), H1 count, canonical (missing, cross-domain, pointing to a broken URL), `noindex`, `<html lang>`, Open Graph, hreflang (valid codes, return links, x-default) and images without `alt`.
- ✅ **WCAG 2.2 accessibility (optional)**: axe-core in a real Chrome browser at level A or AA, with rule, impact, number of failing elements and a help link.
- ✅ **SSL certificate check**: validity, expiry date, days left, issuer and covered names.
- ✅ **Prioritised fix list + reports**: `fixes.csv`, the full `site-qa.csv`, a `site-qa-report.pdf` and a page and site score you can explain.
- ✅ **Uses your sitemap**: seeds the crawl from `sitemap.xml` and robots.txt `Sitemap:` lines.

### Use cases

- **Web agencies and freelancers**: a monthly website health report per client, ready to send as PDF.
- **QA and test teams**: check a site before and after a release or migration for broken links and redirect loops.
- **SEO audits**: find missing titles, meta descriptions, canonicals and hreflang problems across the whole site.
- **Accessibility (EAA / WCAG) preparation**: get the automated part of the WCAG review done and prioritised.
- **Ops**: be warned before an SSL certificate expires.

### How to use it

1. Click **Try for free**, enter the **Start URL** of a site you own or may test, and set **Max pages** (start small, e.g. 50).
2. Optional: set **WCAG level** to A or AA, and turn external link checks on or off.
3. Click **Start**. The **Output** tab has one row per page; the run's storage holds `fixes.csv`, `site-qa.csv` and `site-qa-report.pdf`.
4. For monthly monitoring, save the input as a **task**, add a **schedule**, and send the report link to Slack or e-mail under **Integrations**.

### Example input

```json
{
  "startUrl": "https://books.toscrape.com/",
  "maxPages": 50,
  "maxDepth": 5,
  "checkExternalLinks": true,
  "wcagLevel": "AA",
  "checkSsl": true,
  "requestsPerSecond": 2
}
```

| Field | Default | Notes |
|---|---|---|
| `startUrl` | – | Required. `example.com` becomes `https://example.com/`. |
| `maxPages` | 50 | Hard stop (max 10,000). |
| `maxDepth` | 5 | Link depth from the start URL; `0` = only the start URL. |
| `includePatterns` / `excludePatterns` | `[]` / wp-admin, cart, checkout | Globs: `**` matches anything, `*` anything except `/`. |
| `checkExternalLinks` | `true` | External links are checked (status only), never crawled. |
| `maxLinkChecks` | 1000 | Cap on unique link checks per run. |
| `wcagLevel` | `off` | `off`, `A` or `AA`. A/AA render each page in Chrome. |
| `checkSsl` | `true` | One certificate read per run. |
| `requestsPerSecond` | 2 | Per host, for all requests (1–10). A robots.txt `Crawl-delay` slows it further. |
| `includeSubdomains` | `false` | Off: only the start host (with or without `www.`). |
| `useSitemap` | `true` | Seed the crawl from the sitemap. |

#### Call it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/jordydev~site-qa-crawler/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrl":"https://example.com","maxPages":50,"wcagLevel":"AA"}'
```

### Example output

Real run on books.toscrape.com (WCAG AA on, 28 Oct 2026):

| Page | HTTP | Title | H1s | Page score | Issues found |
|---|---|---|---|---|---|
| https://books.toscrape.com/ | 200 | All products | Books to Scrape - Sandbox | 1 | 79 | No meta description; no canonical; missing Open Graph; WCAG serious: color-contrast (135), target-size (47) |
| https://books.toscrape.com/catalogue/category/books_1/index.html | 200 | Books | Books to Scrape - Sandbox | 1 | 79 | Same as above |

One row as JSON (shortened):

```json
{
  "tool": "site-qa-crawler",
  "target": "https://books.toscrape.com/",
  "status": "ok",
  "httpStatus": 200,
  "title": "All products | Books to Scrape - Sandbox",
  "h1Count": 1,
  "metaDescription": null,
  "canonical": null,
  "brokenLinks": [],
  "linkCounts": { "internal": 73, "external": 0, "notChecked": 51 },
  "issues": [
    { "id": "missing-meta-description", "severity": "medium", "message": "No meta description" },
    { "id": "missing-canonical", "severity": "low", "message": "No canonical link" },
    { "id": "missing-open-graph", "severity": "low", "message": "Missing og:title, og:description, og:image, og:url" },
    { "id": "wcag-serious", "severity": "high", "message": "color-contrast (135), target-size (47)" }
  ],
  "pageScore": 79,
  "billed": true
}
```

The key-value store also holds `RUN_SUMMARY` (site score, SSL, top fixes, totals, link stats, robots and sitemap info, billed events), `site-qa.csv`, `fixes.csv` and `site-qa-report.pdf`.

**How the score works**: page score = 100 minus a weight per issue type on that page (critical 25, high 12, medium 5, low 2), plus up to 15 extra for many broken links on one page. Site score = average page score minus site-wide issues (critical 20, high 10, medium 3, e.g. an expiring certificate).

### How much does it cost?

Pay per event: you only pay per page checked, not for Apify compute.

| Event | Price | When it's charged |
|---|---|---|
| Page crawl | **$0.004** ($4 per 1,000 pages) | A page was fetched and checked (a fetched 404 counts: finding it is the point) |
| WCAG check | **$0.02** | A page was rendered and tested with axe-core (only with `wcagLevel` A/AA, only when the check completed) |
| SSL check | **$0.002** | Once per run, only when a certificate was actually read |
| Actor start | $0.00005 | Apify's standard fee per run start (per GB of memory) |

Examples: 500 pages without accessibility ≈ **$2.00**; 500 pages with WCAG AA ≈ **$12.00**.

**Free**: pages that couldn't be fetched (timeouts, DNS, blocked by robots.txt or the private-address guard) and invalid input. Your maximum cost per run is respected: the crawler checks the budget before each page and stops cleanly at the limit.

### FAQ

**Can I crawl any website? Does it respect robots.txt?**
Use it on sites you own or have permission to test. The crawler always obeys robots.txt (pages *and* link checks; an unreadable robots.txt means "disallow all", per RFC 9309) and `Crawl-delay`, rate-limits every request per host (default 2/s), identifies itself as `SiteQACrawler/0.2`, and refuses private and internal addresses. It stores only page metadata, link results and findings: no cookies, forms or personal data.

**Is this a WCAG / EAA compliance certificate?**
No. Automated tools like axe-core find only part of the WCAG issues. Keyboard use, focus order, alt-text accuracy and screen-reader sense need a human. The report says so too.

**Why did it crawl fewer pages than `maxPages`?**
Pages blocked by robots.txt, outside your patterns, deeper than `maxDepth` or on other subdomains are skipped. `RUN_SUMMARY` explains why.

**Why is it slow?**
It's polite on purpose: 2 requests per second per host by default, including link checks. Raise `requestsPerSecond` (max 10) only on your own site.

**Can it crawl JavaScript-only (SPA) sites?**
Pages are fetched as HTML, so links that only appear after JavaScript runs aren't followed. WCAG checks do render the page in Chrome.

**Does it judge content or rankings?**
No keyword scoring and no invented ranking grade: only checkable facts.

### Related Actors

More tools by the same developer, all pay-per-event with the same clean output style:

- [EU Jobs Search (Workday, Greenhouse, Lever & Teamtailor)](https://apify.com/jordydev/eu-job-search): search live jobs from 6,500+ European employers' career sites by keyword and country, no company list needed.
- [Greenhouse & Lever Jobs Scraper (ATS Job Feed)](https://apify.com/jordydev/ats-job-feed): every open job from your own list of companies on Greenhouse, Lever, Ashby, Workable, Recruitee and Personio.
- [Bulk PageSpeed Insights & Core Web Vitals Checker](https://apify.com/jordydev/pagespeed-bulk-audit): Lighthouse scores, LCP/CLS/INP and top fixes for hundreds of URLs, with CSV and PDF report.
- [Website Screenshot & HTML to PDF](https://apify.com/jordydev/screenshot-pdf): full-page PNG/JPEG screenshots and PDFs of any URL or raw HTML, cookie banners removed.

***

Related searches: broken link checker, dead link checker, website audit tool, SEO crawler, technical SEO audit, redirect chain checker, WCAG accessibility checker, axe-core scanner, SSL certificate expiry checker, website QA report PDF, Screaming Frog alternative.

# Actor input Schema

## `startUrl` (type: `string`):

The site to crawl. Crawl only sites you own or have permission to test. robots.txt is always respected; private and internal addresses are never fetched.

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

Stop after this many pages. Each fetched page is one 'page-crawled' event ($0.004).

## `maxDepth` (type: `integer`):

How many links deep to follow. 0 = the start URL only (no sitemap). Sitemap pages count as depth 1.

## `includePatterns` (type: `array`):

Only crawl URLs matching one of these globs, e.g. https://example.com/blog/\*\*. \*\* matches anything, \* anything except '/'.

## `excludePatterns` (type: `array`):

Skip URLs matching these globs (they can still be link-checked).

## `checkExternalLinks` (type: `boolean`):

Also check links to other sites (HEAD, GET fallback). External sites are never crawled, and their robots.txt is respected.

## `maxLinkChecks` (type: `integer`):

Cap on unique links checked that were not crawled as pages. Link checks are included in the page price.

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

Run axe-core in Chromium on every HTML page. One extra 'wcag-check' event ($0.02) per page checked.

## `checkSsl` (type: `boolean`):

Read the start host's TLS certificate (expiry, issuer, trust). One 'ssl-check' event ($0.002) only when a certificate was read.

## `requestsPerSecond` (type: `integer`):

Politeness limit applied to every request: pages, link checks, robots.txt, sitemaps and same-host browser requests. Keep it low on sites you do not control.

## `includeSubdomains` (type: `boolean`):

Treat subdomains of the start host (e.g. blog.example.com) as part of the site.

## `useSitemap` (type: `boolean`):

Seed the crawl from robots.txt Sitemap: lines, /sitemap.xml and /sitemap_index.xml (same-site URLs only).

## `saveCsv` (type: `boolean`):

Save site-qa.csv (pages) and fixes.csv to the key-value store.

## `savePdf` (type: `boolean`):

Save a client-ready site-qa-report.pdf to the key-value store.

## Actor input object example

```json
{
  "startUrl": "https://books.toscrape.com/",
  "maxPages": 50,
  "maxDepth": 5,
  "includePatterns": [],
  "excludePatterns": [
    "**/wp-admin/**",
    "**/cart/**",
    "**/checkout/**"
  ],
  "checkExternalLinks": true,
  "maxLinkChecks": 1000,
  "wcagLevel": "off",
  "checkSsl": true,
  "requestsPerSecond": 2,
  "includeSubdomains": false,
  "useSitemap": true,
  "saveCsv": true,
  "savePdf": true
}
```

# Actor output Schema

## `pages` (type: `string`):

No description

## `report` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `fixes` (type: `string`):

No description

## `summary` (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 = {
    "startUrl": "https://books.toscrape.com/"
};

// Run the Actor and wait for it to finish
const run = await client.actor("jordydev/site-qa-crawler").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 = { "startUrl": "https://books.toscrape.com/" }

# Run the Actor and wait for it to finish
run = client.actor("jordydev/site-qa-crawler").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 '{
  "startUrl": "https://books.toscrape.com/"
}' |
apify call jordydev/site-qa-crawler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jordydev/site-qa-crawler"
        }
    }
}
```

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/iQYC0Dt8dVrD2OFHu/builds/XPI7noXpajiSSwB9z/openapi.json
