# Broken Link Checker & SEO Audit: 404s, Redirects, Sitemap (`gmyri/site-health-audit`) Actor

Crawl your website and find broken links (404s), redirect chains, sitemap errors and on-page SEO issues (titles, meta, H1, canonical, noindex). Markdown report. $2 per 1,000 pages.

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

## Pricing

from $2.00 / 1,000 audited pages

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 Audit: 404s, Redirects, Sitemap

A **broken link checker and SEO audit** for your own website: crawls the sitemap and pages, finds **404s and broken links** (with the pages they're on), redirect chains and on-page SEO problems, and writes a Markdown report.

💲 **$2 per 1,000 pages audited** plus $0.30 per 1,000 extra external links checked. Pay only for results. No subscription, and the Apify free plan's monthly credit covers small jobs.

**Also known as:** broken link checker, dead link checker, 404 checker, website SEO audit, site audit, sitemap validator, redirect checker, on-page SEO checker, Screaming Frog alternative.

### Quick start

1. Click **Start**. The prefilled input works as is.
2. Or paste this input (JSON tab):

```json
{
  "startUrl": "https://books.toscrape.com/",
  "maxPages": 10,
  "maxLinksToCheck": 50
}
```

**Output** (one dataset item):

```json
{
  "type": "brokenLink",
  "url": "https://example.com/old-page",
  "status": 404,
  "isInternal": true,
  "foundOnCount": 3,
  "foundOn": [
    "https://example.com/blog/post-1"
  ]
}
```

### Use it from the API, AI agents (MCP), n8n, Make or Zapier

```bash
curl -X POST "https://api.apify.com/v2/acts/gmyri~site-health-audit/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"startUrl":"https://www.example.com/","maxPages":200}'
```

- **AI agents / MCP:** connect `https://mcp.apify.com/?actors=gmyri/site-health-audit` to Claude, Cursor, ChatGPT or any MCP client. The input schema is fully described for tool calling.
- **Python:** `ApifyClient(token).actor("gmyri/site-health-audit").call(run_input={...})`
- **n8n / Make / Zapier:** use the Apify "Run Actor" action with `gmyri/site-health-audit`, then read the dataset items.

### Quick FAQ

- **Should I audit sites I don't own?** This tool is meant for your own sites or sites you have permission to audit. It respects robots.txt by default and identifies itself in the user agent.
- **Can I run it on a schedule?** Yes. Create a Task with your input and add a weekly schedule. Use `REPORT.md` and the dataset to track issues over time.
- **What if I set a spending limit?** The Actor stops cleanly at your *Max cost per run* and you're never charged above it.

### Details

Audit **your own website** in a single run:

- sitemap discovery and validation
- HTTP status codes and redirect chains
- **broken internal and external links**, with the pages they appear on
- titles, meta descriptions, H1s, canonicals, `noindex`, JSON-LD, alt text, mixed content, `lang`, viewport and thin content

You get a clean dataset, a readable **Markdown report**, and a JSON summary you can track over time. You pay per page, with no subscription.

### Why this Actor

Today you need three Actors (a sitemap extractor, a link checker and an SEO auditor) and you pay up to $0.04 per page. This Actor does all three jobs in one pass, for **$0.002 per page**. It is polite by default: it respects robots.txt, uses 4 parallel requests and identifies itself with its own user agent.

### What it checks

| Level | Checks |
|---|---|
| Site | robots.txt present, sitemap(s) found (robots.txt, `/sitemap.xml`, `/sitemap_index.xml`, nested index files, `.gz`), sitemap URLs that do not return 200, sitemap URLs on another host, http → https redirect, URLs blocked by robots.txt |
| Page | Status code, redirect chain, response time, content type and size, `<title>` (missing/short/long/duplicate), meta description (missing/long/duplicate), H1 (missing/multiple), canonical pointing elsewhere, `noindex` (flagged as a warning when the page is in the sitemap), JSON-LD types and parse errors, images without `alt`, `http://` resources on https pages, `<html lang>`, viewport, word count, internal/external link counts, depth |
| Links | Every link found is checked once (HEAD, falling back to GET). Broken links (4xx/5xx/network errors) are listed with up to 10 source pages each. |

Every issue has a `severity` (`error`, `warning` or `notice`), a stable `code` (for example `BROKEN_LINKS_ON_PAGE`, `DUPLICATE_TITLE`) and a human-readable `message`, so you can filter results or alert on them.

### Input

```json
{
  "startUrl": "https://www.example.com/",
  "discovery": "sitemapAndCrawl",
  "maxPages": 500,
  "checkExternalLinks": true,
  "maxLinksToCheck": 2000,
  "excludeUrlPatterns": ["/tag/", "\\?replytocom="],
  "maxConcurrency": 4
}
```

| Field | Default | Description |
|---|---|---|
| `startUrl` | – | Homepage of the site to audit |
| `discovery` | `sitemapAndCrawl` | `sitemapOnly`, `crawlOnly` or both |
| `maxPages` | 100 | Pages to audit. Each one is a `page-audited` event. |
| `checkExternalLinks` | `true` | Verify outbound links |
| `maxLinksToCheck` | 1000 | Cap on extra link checks (`link-checked` events) |
| `includeSubdomains` | `false` | Treat `*.example.com` as internal |
| `excludeUrlPatterns` | – | Regexes of URLs to skip |
| `respectRobotsTxt` | `true` | Skip disallowed URLs |
| `maxConcurrency` | 4 | Parallel requests (1–20) |
| `slowResponseMs` | 2000 | Threshold for `SLOW_RESPONSE` |
| `userAgent` | `SiteHealthAudit/0.1` | Shown in your server logs |
| `outputAllLinks` | `false` | Also write every checked link, not only broken ones |

### Output

- **Dataset.** Items with `type: "page"` (one per audited page, worst pages first) and `type: "brokenLink"`:
  ```json
  { "type": "page", "url": "https://www.example.com/blog/post1", "status": 200, "title": "…", "titleLength": 68,
    "metaDescription": "…", "h1Count": 1, "canonical": "https://www.example.com/blog/", "noindex": true,
    "inSitemap": true, "jsonLdTypes": [], "jsonLdErrors": 1, "wordCount": 812, "responseTimeMs": 143,
    "issues": [{ "severity": "error", "code": "INVALID_JSON_LD", "message": "1 JSON-LD block(s) fail to parse" }] }
  ```
  ```json
  { "type": "brokenLink", "url": "https://www.example.com/missing", "status": 404, "isInternal": true,
    "foundOnCount": 3, "foundOn": ["https://www.example.com/", "…"] }
  ```
- **`REPORT.md`** (key-value store): a Markdown report you can paste into a ticket or send to a client.
- **`OUTPUT`** (key-value store): a summary with site-level issues, issue counts, pages audited and broken links. It is handy for scheduled monitoring.

### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| `page-audited` | **$0.002** | Each page fetched and analysed |
| `link-checked` | $0.0003 | Each extra link verified that is not itself audited (external links, or internal links beyond `maxPages`) |
| `apify-actor-start` | $0.00005 | Platform default, once per run |

Example: a 500-page site with 1,500 external links costs 500 × $0.002 + 1,500 × $0.0003 = **$1.45**. If you set a maximum cost per run, the Actor sizes the crawl to fit it up front and stops cleanly when the limit is reached.

### Scheduling and monitoring

Schedule the Actor weekly and add an integration (Slack, email or webhook) on `OUTPUT.brokenLinks > 0`. To call it from an AI agent, use `https://mcp.apify.com/?tools=<your-username>/site-health-audit`. The Actor uses pay-per-event pricing only and limited permissions, with no Standby, so it is eligible for agentic payments.

### Responsible use

Use this Actor only on websites you own or are authorised to audit. By default it respects `robots.txt`, keeps concurrency low and identifies itself in the `User-Agent`. It does not log in, render JavaScript or bypass any access control.

### Limitations

- HTML is analysed as served. JavaScript-rendered content (client-side SPAs) is not executed.
- Links are taken from `<a href>`. Links inside JSON or JavaScript are not discovered.

### Related tools

- [DNS Lookup & SSL Checker: SPF, DMARC, DKIM, Domain Expiry](https://apify.com/gmyri/domain-health-check)
- [Website Screenshot API: Full Page, Mobile & URL to PDF](https://apify.com/gmyri/website-screenshot)
- [PDF to Markdown & Text Converter: Word, Excel, PPT, HTML, RAG](https://apify.com/gmyri/doc-to-markdown)

# Actor input Schema

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

Homepage of the site you own or are authorised to audit, e.g. https://www.example.com/

## `discovery` (type: `string`):

Sitemaps are discovered from robots.txt, /sitemap.xml and /sitemap_index.xml (index files and .gz supported).

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

Each audited page is one 'page-audited' event.

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

Also verify outbound links (HEAD, falling back to GET).

## `maxLinksToCheck` (type: `integer`):

Links that are not audited as pages (external, or internal beyond maxPages). Each is one 'link-checked' event.

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

If enabled, blog.example.com counts as part of example.com.

## `excludeUrlPatterns` (type: `array`):

URLs matching any of these regular expressions are not audited, e.g. '/tag/' or '\\?page='.

## `respectRobotsTxt` (type: `boolean`):

Skip URLs disallowed for the user agent below.

## `maxConcurrency` (type: `integer`):

Keep this low to be gentle on your server.

## `slowResponseMs` (type: `integer`):

Pages slower than this get a SLOW_RESPONSE warning.

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

Per-request timeout.

## `userAgent` (type: `string`):

Identify the audit in your server logs. Leave empty for the default 'SiteHealthAudit/0.1' agent.

## `outputAllLinks` (type: `boolean`):

By default only broken links are written to the dataset.

## Actor input object example

```json
{
  "startUrl": "https://books.toscrape.com/",
  "discovery": "sitemapAndCrawl",
  "maxPages": 10,
  "checkExternalLinks": true,
  "maxLinksToCheck": 50,
  "includeSubdomains": false,
  "respectRobotsTxt": true,
  "maxConcurrency": 4,
  "slowResponseMs": 2000,
  "requestTimeoutSecs": 20,
  "outputAllLinks": false
}
```

# Actor output Schema

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

One item per audited page (type=page) and per broken link (type=brokenLink).

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

Human-readable audit report (REPORT.md).

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

OUTPUT record: site-level checks, issue counts, totals.

# 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/",
    "maxPages": 10,
    "maxLinksToCheck": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("gmyri/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 = {
    "startUrl": "https://books.toscrape.com/",
    "maxPages": 10,
    "maxLinksToCheck": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("gmyri/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 '{
  "startUrl": "https://books.toscrape.com/",
  "maxPages": 10,
  "maxLinksToCheck": 50
}' |
apify call gmyri/site-health-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gmyri/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/IZREVaqkp3Tt1TUuy/builds/6347cGV3pDgqQfD7G/openapi.json
