# Broken Link Checker & Site Auditor (`andok/broken-links-checker`) Actor

Crawl websites to detect 404 broken links and missing resources. Essential for maintaining technical SEO and user experience.

- **URL**: https://apify.com/andok/broken-links-checker.md
- **Developed by:** [Andok](https://apify.com/andok) (community)
- **Categories:** SEO tools, Developer tools
- **Features:** Limited permissions
- **Stats:** 5 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.47 / 1,000 page crawleds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Links Checker

Find every broken link on your website before visitors and search engines do. Broken links tank user experience and leak link equity during site migrations — this actor crawls your pages, checks every link (internal and external), and reports exactly which URLs return 404, 5xx, or timeout errors. Configure crawl depth, page limits, and concurrency to match sites of any size.

### Features

- **Deep crawling** — follows internal links up to a configurable depth to discover broken links across your entire site
- **Internal and external** — optionally checks outbound links to third-party sites, not just internal pages
- **Smart link checking** — uses HEAD requests first, falls back to GET when servers block HEAD
- **Per-page reporting** — groups broken links by the page they appear on for easy triage
- **Redirect chain tracking** — captures the full redirect chain for each broken link
- **Same-domain filtering** — restrict crawling to the start URL's domain or allow cross-domain discovery
- **Configurable limits** — set max pages, max depth, and max links per page to control scope and cost

### Input

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `startUrls` | `array` | Yes | — | Pages to begin crawling from (request list format) |
| `maxPages` | `integer` | No | `100` | Maximum number of pages to crawl |
| `maxDepth` | `integer` | No | `3` | How many link levels deep to follow from the start URLs |
| `sameDomainOnly` | `boolean` | No | `true` | Only crawl pages on the same domain as the start URL |
| `checkExternalLinks` | `boolean` | No | `false` | Also check outbound links to external domains |
| `maxLinksPerPage` | `integer` | No | `200` | Maximum number of links to check on each crawled page |
| `timeoutSeconds` | `integer` | No | `20` | HTTP timeout in seconds for each link check |
| `concurrency` | `integer` | No | `5` | Number of pages to process in parallel |
| `userAgent` | `string` | No | `Mozilla/5.0 (compatible; ApifyActor/1.0; +https://apify.com)` | Custom User-Agent header for HTTP requests |

#### Input Example

```json
{
    "startUrls": [{ "url": "https://crawlee.dev" }],
    "maxPages": 200,
    "maxDepth": 3,
    "checkExternalLinks": true,
    "sameDomainOnly": true
}
```

### Output

Each crawled page produces one dataset item listing all broken links found on that page.

- `pageUrl` (`string`) — the page that was crawled
- `depth` (`number`) — how many links deep from the start URL
- `pageStatus` (`number | null`) — HTTP status of the crawled page itself
- `brokenCount` (`number`) — total broken links found on this page
- `brokenLinks` (`array`) — list of broken links with `url`, `status`, `finalUrl`, `redirectChain`, and `error`
- `checkedAt` (`string`) — ISO timestamp
- `error` (`string | null`) — error if the page itself could not be fetched

#### Output Example

```json
{
    "pageUrl": "https://crawlee.dev/docs/introduction",
    "depth": 1,
    "pageStatus": 200,
    "brokenCount": 2,
    "brokenLinks": [
        {
            "url": "https://crawlee.dev/docs/old-guide",
            "status": 404,
            "finalUrl": "https://crawlee.dev/docs/old-guide",
            "redirectChain": [],
            "error": null
        },
        {
            "url": "https://external-site.com/removed-page",
            "status": null,
            "finalUrl": null,
            "redirectChain": [],
            "error": "ECONNREFUSED"
        }
    ],
    "checkedAt": "2025-11-20T14:30:00.000Z",
    "error": null
}
```

### Pricing

| Event | Cost |
|-------|------|
| Page Crawled | $0.001 per page |

You are charged per page crawled. Platform usage fees apply separately.

### Use Cases

- **Pre-migration audits** — scan your entire site before a domain or CMS migration to establish a baseline
- **Post-migration validation** — verify no links broke after launching on a new domain or URL structure
- **SEO health checks** — find and fix broken internal links that waste crawl budget and leak link equity
- **Client reporting** — agencies can schedule weekly runs to catch broken links before clients notice
- **External link monitoring** — detect when third-party resources you link to go offline

### Related Actors

| Actor | What it adds |
|-------|-------------|
| [Redirect Chain Analyzer](https://apify.com/andok/redirect-chain-analyzer) | Trace the full redirect chain for URLs to diagnose redirect loops and chains |
| [Hreflang Checker](https://apify.com/andok/hreflang-checker) | Validate hreflang tags on multilingual pages found during crawling |
| [XML Sitemap URL Extractor](https://apify.com/andok/sitemap-extractor) | Extract all URLs from your sitemap to use as start URLs for a comprehensive crawl |

# Actor input Schema

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

One or more pages to begin crawling from. The crawler will follow internal links from these pages up to the configured depth.

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

Maximum number of pages to crawl. Each crawled page counts as one billable event. Increase for larger sites.

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

How many link levels deep to follow from the start URLs. Set to 0 to only check links on the start pages themselves.

## `sameDomainOnly` (type: `boolean`):

When enabled, only crawls pages on the same domain as the start URL. Disable to follow links across subdomains or related sites.

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

Also check outbound links pointing to external domains. Enabling this increases run time but catches broken third-party references.

## `maxLinksPerPage` (type: `integer`):

Maximum number of links to check on each crawled page. Pages with many navigation links may benefit from a higher limit.

## `timeoutSeconds` (type: `integer`):

HTTP timeout in seconds for each individual link check. Increase for slow-responding servers.

## `concurrency` (type: `integer`):

Number of pages to crawl and check simultaneously. Higher values speed up large crawls but use more memory.

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

Custom User-Agent header sent with every HTTP request. Some servers block requests from non-browser user agents.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://crawlee.dev"
    }
  ],
  "maxPages": 100,
  "maxDepth": 3,
  "sameDomainOnly": true,
  "checkExternalLinks": false,
  "maxLinksPerPage": 200,
  "timeoutSeconds": 20,
  "concurrency": 5,
  "userAgent": "Mozilla/5.0 (compatible; ApifyActor/1.0; +https://apify.com)"
}
```

# Actor output Schema

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

No description

## `resultsCsv` (type: `string`):

No description

## `run` (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 = {
    "startUrls": [
        {
            "url": "https://crawlee.dev"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("andok/broken-links-checker").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://crawlee.dev" }] }

# Run the Actor and wait for it to finish
run = client.actor("andok/broken-links-checker").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://crawlee.dev"
    }
  ]
}' |
apify call andok/broken-links-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,andok/broken-links-checker"
        }
    }
}
```

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/XBRdohFoDgfJ2ZNDq/builds/Sa6K8zmq7jVFbpv58/openapi.json
