# Broken Link Checker – Website 404 & Dead Link Finder (`pinkish_armada/broken-link-checker`) Actor

Crawl your website and find broken links (404s, dead domains, timeouts, invalid URLs), redirects and links that block bots, with every page and anchor text where each broken link appears.

- **URL**: https://apify.com/pinkish_armada/broken-link-checker.md
- **Developed by:** [Phuc Tiet](https://apify.com/pinkish_armada) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 85.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 page scanneds

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 – Website 404 & Dead Link Finder

Find every **broken link on your website**: 404 pages, dead domains, timeouts, server errors and malformed URLs. For each one you get **the pages where it appears and the anchor text**, so you can fix it in minutes.

Give it your homepage. It crawls your internal pages, collects every link, checks each unique link once, and gives you a clean report.

### Why use it

- **Fix-ready report** – every broken link comes with `foundOn` (up to 20 pages, with anchor text) and `foundOnCount`.
- **Fewer false alarms** – sites like LinkedIn or Cloudflare-protected pages often answer bots with 401/403/429/999 even though the link works. These are reported separately as `blocked`, not mixed into your broken links.
- **Simple, predictable pricing** – you pay **per page scanned**, and all link checks are included. One link used on 500 pages is checked once.
- **Polite by default** – respects your robots.txt, uses modest concurrency and retries with backoff, and checks links with lightweight HEAD requests (GET fallback).

### What it checks

- `<a href>` links, internal and external (external checks can be turned off).
- Optionally images, scripts, stylesheets, icons, iframes and media (`checkResources`).
- Status codes, DNS failures (`ENOTFOUND`), refused connections, timeouts, invalid URLs and redirect chains.

Verdicts:

| Verdict | Meaning |
|---|---|
| `broken` | 4xx/5xx (except below), DNS error, timeout, connection error or invalid URL |
| `blocked` | 401, 403, 429 or 999 – the server refused an automated check; verify manually |
| `redirect` | Works, but through one or more redirects (good to update) |
| `skipped` | Internal URL disallowed by robots.txt (not requested) |
| `ok` | 2xx response |

### Input

| Field | Default | Description |
|---|---|---|
| `startUrls` | – | Your homepage or any page (required). |
| `maxPages` | 100 | Max internal pages to scan (also caps cost). |
| `checkExternalLinks` | true | Check links to other sites. |
| `checkResources` | false | Also check images/scripts/CSS/media. |
| `onlyBrokenLinks` | true | Save only broken links, or every link with its verdict. |
| `reportBlockedAsBroken` | false | Count 401/403/429/999 as broken. |
| `maxDepth` | 0 | Clicks from the start page (0 = unlimited). |
| `includeSubdomains` | false | Crawl subdomains as internal. |
| `excludePatterns` | \[] | Substrings or `/regex/` to skip. |
| `respectRobotsTxt` | true | Don't crawl disallowed pages. |
| `maxConcurrency` | 5 | Parallel page downloads. |

```json
{ "startUrls": ["https://example.com"], "maxPages": 500, "checkResources": true }
```

### Output

```json
{
  "url": "https://example.com/old-pricing",
  "verdict": "broken",
  "status": 404,
  "error": null,
  "finalUrl": "https://example.com/old-pricing",
  "redirectCount": 0,
  "isInternal": true,
  "linkTypes": ["a"],
  "foundOnCount": 3,
  "foundOn": [{ "pageUrl": "https://example.com/blog/post-1", "text": "see our pricing" }],
  "firstFoundOn": "https://example.com/blog/post-1",
  "anchorText": "see our pricing",
  "responseTimeMs": 211
}
```

The key-value store record `OUTPUT` contains the run summary: pages scanned, unique links, counts per verdict, and the list of scanned pages with their status and number of links.

### How much does it cost to check a website for broken links?

Pay per event:

- **Page scanned**: $0.002 per page ($2.00 per 1,000 pages). Checking every link on those pages, internal and external, is included.
- A tiny Actor start fee: $0.00005 per GB of memory.

Platform usage is included in the price. To control spending, set `maxPages` or a maximum cost per run. If the limit is reached, crawling stops, and the links already found are still checked and reported.

### Good to know

- Use it on websites you own or manage. It is a site-maintenance tool, not a scraper.
- JavaScript-rendered links (content injected by client-side JS only) are not seen. Server-rendered and static sites, WordPress, Shopify, Webflow, docs sites and most blogs work well.
- Some servers rate-limit or block automated checks. Those links show up as `blocked` or `TIMEOUT`. Lower `maxConcurrency` for small servers.
- A link is checked once per run, even if it appears on many pages.

### FAQ

**How do I find broken links on my whole website?** Enter your homepage in `startUrls`, set `maxPages` high enough to cover the site, and run. Every internal page is crawled and every link on it (internal and external) is checked once.

**Does it check external links too?** Yes, by default. Turn off `checkExternalLinks` to check only links within your own domain.

**Which links count as broken?** HTTP 4xx and 5xx responses, DNS failures, timeouts and connection errors. Redirects are reported separately so you can update them.

**Can I schedule it?** Yes. Create a schedule in Apify Console (for example weekly) and add an email or Slack integration to get the report automatically.

**How much does a full-site check cost?** $0.002 per page crawled, so a 500-page site costs about $1.00, with all link checks included.

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

- **AI agents and MCP clients** (Claude, ChatGPT, Cursor, VS Code): add `https://mcp.apify.com?tools=pinkish_armada/broken-link-checker` as an MCP server, and your agent can find broken links and 404s on a website as a tool call.
- **API**: run it and get the results in one request:

```bash
curl -X POST "https://api.apify.com/v2/acts/pinkish_armada~broken-link-checker/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrls":["https://crawlee.dev"],"maxPages":100}'
```

- **No-code**: use the official Apify apps for **Make**, **Zapier** and **n8n** ("Run an Actor"), then send results to Google Sheets, Slack, Airtable or your database. You can also schedule runs in Apify Console and add a webhook or email integration.

### Related tools

- [Sitemap URL Extractor](https://apify.com/pinkish_armada/sitemap-url-extractor): list every URL of a site from its sitemaps, with optional 404 checks
- [PDF to Markdown](https://apify.com/pinkish_armada/pdf-to-markdown): turn PDFs (including scans, via OCR) into Markdown, text and tables
- [Office Docs to Markdown](https://apify.com/pinkish_armada/office-to-markdown): turn DOCX, PPTX, XLSX, HTML and EPUB into clean Markdown for LLMs and RAG
- [Domain Lookup](https://apify.com/pinkish_armada/domain-intel): bulk RDAP WHOIS, DNS, SPF/DMARC and SSL-expiry checks for domains

# Actor input Schema

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

Homepage or any page of the website you want to check. The crawler follows internal links from here. Please check sites you own or manage.

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

Maximum number of internal HTML pages to download and scan for links. You are charged per scanned page, so this also caps cost. Internal links beyond this limit are still checked (lightweight request) but not scanned.

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

Also check links pointing to other websites (one lightweight request per unique link).

## `checkResources` (type: `boolean`):

Also check <code>img</code>, <code>script</code>, stylesheet, icon, iframe and media URLs, not just <code>\<a href></code> links.

## `onlyBrokenLinks` (type: `boolean`):

When on, the dataset contains only broken links. Turn off to get every checked link with its verdict (ok, redirect, blocked, broken).

## `reportBlockedAsBroken` (type: `boolean`):

Many sites (social networks, Cloudflare-protected sites) answer automated requests with 401/403/429/999 even though the link works in a browser. By default these are reported as <code>blocked</code> and not as broken.

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

How many clicks away from the start URL to crawl. 0 = unlimited (bounded by Max pages).

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

Crawl pages on subdomains (e.g. blog.example.com when starting at example.com).

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

Skip links matching any pattern. Plain text = substring, <code>/regex/</code> = regular expression. Example: <code>/wp-admin/</code>, <code>?replytocom=</code>.

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

Do not crawl pages disallowed by the site's robots.txt. You can turn this off for your own site.

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

Parallel page downloads (link checks use twice this). Keep it low to be gentle on small servers.

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

Links that don't respond within this time (after retries) are reported as broken with error TIMEOUT.

## Actor input object example

```json
{
  "startUrls": [
    "https://crawlee.dev"
  ],
  "maxPages": 10,
  "checkExternalLinks": true,
  "checkResources": false,
  "onlyBrokenLinks": true,
  "reportBlockedAsBroken": false,
  "maxDepth": 0,
  "includeSubdomains": false,
  "excludePatterns": [],
  "respectRobotsTxt": true,
  "maxConcurrency": 5,
  "requestTimeoutSecs": 20
}
```

# Actor output Schema

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

Broken (or all) links with verdict, status code, error, internal/external flag and foundOn pages with anchor text.

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

Pages scanned, unique links, counts per verdict (ok/redirect/broken/blocked/skipped) and the list of scanned pages.

# 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": [
        "https://crawlee.dev"
    ],
    "maxPages": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("pinkish_armada/broken-link-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": ["https://crawlee.dev"],
    "maxPages": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("pinkish_armada/broken-link-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": [
    "https://crawlee.dev"
  ],
  "maxPages": 10
}' |
apify call pinkish_armada/broken-link-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pinkish_armada/broken-link-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/kli0jJhHT1uIdAlwS/builds/ZCu41ZX1xdC10Loem/openapi.json
