# Broken Link Checker for 404s, Dead Links & Redirects (`goldish_fireplace/broken-link-checker`) Actor

Find broken links, 404 errors, dead external links, broken images and redirect chains on any website. Shows the page and link text for each. Fast, no browser.

- **URL**: https://apify.com/goldish_fireplace/broken-link-checker.md
- **Developed by:** [goldish_fireplace](https://apify.com/goldish_fireplace) (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

from $0.42 / 1,000 link checkeds

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 Link Checker: Find 404s, Dead Links, Broken Images & Redirect Chains

**Online broken link checker and dead link finder for whole websites.** Enter a URL, and it crawls your site, checks every internal and external link, image, script and stylesheet, and returns a list of **404 errors, dead external links, broken images and redirect chains**, each with the page it is on and the link text. Export to CSV, Excel, JSON or a shareable HTML report.

**Broken Link Checker** crawls your website and checks **every link, image, script and stylesheet**. It finds **broken links (404, 410, 500…)**, **dead external links**, **broken images**, **redirect chains**, **redirect loops**, **timeouts** and **DNS errors**. For each problem it tells you **which page it is on** and **the link text**, so you can fix it in minutes.

It is a fast website link auditor that uses plain HTTP and no browser, so a full scan costs a fraction of a cent per page. You can use it for SEO audits, QA checks before a release, content migrations, or monthly site health monitoring.

### What does Broken Link Checker do?

- 🔎 **Crawls your site.** It follows internal links from your start URL, up to the page limit you set.
- 🔗 **Checks every unique URL once.** That covers internal pages, external links, `<img>`, `<script>`, stylesheets, `<iframe>`, video and audio.
- 🧭 **Shows the full redirect chain.** You see each hop with its status code, so you can replace 301/302 chains with direct links.
- 🛟 **Avoids false alarms.** It tries `HEAD` first and falls back to `GET` when a server rejects `HEAD`. It retries `429` and `5xx` responses and honours `Retry-After`. Sites that block bots (`401`, `403`, `429`, LinkedIn's `999`) are reported as **uncertain**, not broken.
- 📍 **Tells you where to fix it.** For each link you get the first page it was found on, the anchor text, how many times it is linked, and up to 10 source pages.
- 📊 **Gives you a summary report.** The `SUMMARY` record has totals per result plus **broken links grouped by page**.
- 🖥️ **Gives you a shareable HTML report.** Open the **HTML report** in the Output tab: totals at the top, then every broken link, redirect and uncertain link with the page it is on. Send it to a client or teammate as is.
- 🤖 **Crawls politely.** It respects `robots.txt` when crawling and has per-host concurrency limits. External sites are only pinged, never crawled.

### Use cases

- **SEO audits**: broken links and long redirect chains waste crawl budget and hurt user experience.
- **QA and release testing**: catch dead links and missing assets before or after a deployment. You can run it from CI through the Apify API.
- **Website migrations**: confirm that old URLs redirect correctly and nothing returns 404.
- **Content teams and agencies**: run monthly scheduled audits of client websites and export the results to CSV or Excel.
- **Documentation sites**: find links to moved or deleted pages.

### How to use it

1. Enter your website in **Website URLs**, e.g. `https://example.com`.
2. Optionally set **Max pages to crawl** and **What to output** (broken only, problems, or all links).
3. Click **Start**. When the run finishes, open the **Problems overview** table, or download the results as JSON, CSV, Excel or HTML.

### Input example

```json
{
    "startUrls": [{ "url": "https://example.com/" }],
    "maxPagesToCrawl": 500,
    "maxLinksToCheck": 5000,
    "outputMode": "problems",
    "checkExternalLinks": true,
    "checkAssets": true,
    "excludeUrlPatterns": ["/tag/", "\\?replytocom="]
}
```

| Field | Default | What it does |
|---|---|---|
| `startUrls` | (required) | Pages to start crawling from |
| `maxPagesToCrawl` | 100 | Pages of your site to open and scan |
| `maxLinksToCheck` | 5000 | Cap on unique URLs checked. This also caps your cost |
| `outputMode` | `problems` | `broken`, `problems` (broken + redirects + uncertain) or `all` |
| `checkExternalLinks` | true | Check links to other websites |
| `checkAssets` | true | Check images, scripts, stylesheets, iframes and media |
| `includeSubdomains` | false | Crawl `blog.example.com` as part of `example.com` |
| `respectRobotsTxt` | true | Skip pages disallowed by robots.txt when crawling |
| `excludeUrlPatterns` | \[] | Regex or plain-text patterns to skip |

### Output example

Each dataset item is one checked URL:

```json
{
    "url": "https://the-internet.herokuapp.com/asdf.jpg",
    "result": "broken",
    "status": 404,
    "statusText": "Not Found",
    "error": null,
    "finalUrl": "https://the-internet.herokuapp.com/asdf.jpg",
    "redirectChain": [],
    "method": "GET",
    "responseTimeMs": 412,
    "isInternal": true,
    "element": "img",
    "foundOn": "https://the-internet.herokuapp.com/broken_images",
    "anchorText": null,
    "occurrences": 1,
    "sources": [{ "pageUrl": "https://the-internet.herokuapp.com/broken_images", "anchorText": null, "element": "img" }],
    "checkedAt": "2026-11-01T04:09:19.742Z"
}
```

`result` is one of:

| result | meaning |
|---|---|
| `broken` | 4xx/5xx response (e.g. 404 Not Found, 410 Gone, 500) |
| `error` | No valid response: DNS failure, timeout, connection refused, redirect loop (`error` field gives the code) |
| `redirect` | Works, but only after one or more redirects (see `redirectChain`) |
| `uncertain` | 401/403/429/999. The site requires login or blocks automated checks, so verify it manually |
| `ok` | 2xx response (only output in `all` mode) |

The **SUMMARY** record in the key-value store looks like this:

```json
{
    "pagesCrawled": 60,
    "linksChecked": 230,
    "results": { "ok": 210, "redirect": 2, "broken": 14, "error": 1, "uncertain": 3 },
    "pagesWithBrokenLinks": 14,
    "brokenByPage": { "https://example.com/hovers": ["https://example.com/users/1"] }
}
```

### Pricing

This Actor uses **pay-per-event** pricing. You pay a small fixed amount **per unique link checked** (see the **Pricing** tab), whatever output mode you choose. Platform compute and proxy-free HTTP requests are included, so there are no surprise usage costs.

| What you scan | Typical unique links | Cost |
| --- | --- | --- |
| Small site (20 pages) | ~300 | 300 × price per link |
| Medium site (100 pages) | ~1,500 | 1,500 × price per link |
| Large site (1,000 pages) | ~10,000 | 10,000 × price per link |

You are never charged twice for a link that appears on many pages.

- Example: a 100-page site with about 1,500 unique links costs well under a dollar.
- Set **Max links to check** or a maximum cost per run, and the Actor stops cleanly at your limit. It still saves every result checked up to that point.

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

- **AI agents (MCP):** Broken Link Checker works with the [Apify MCP server](https://mcp.apify.com), so Claude, Cursor, ChatGPT and other MCP clients can find it and run it for you. Ask something like *"Run Broken Link Checker on example.com and list what to fix."*
- **No-code:** use the Apify app in **Zapier**, **Make** or **n8n** to start a run and send the results to Google Sheets, Slack or email.
- **API and CI:** start a run with one HTTP call (`POST https://api.apify.com/v2/acts/goldish_fireplace~broken-link-checker/run-sync-get-dataset-items`) and read `SUMMARY` to pass or fail a pipeline.
- **Schedules:** run it weekly or monthly with Apify Schedules and get notified when new problems appear.

### Limitations

- **No JavaScript rendering.** Links that only appear after JavaScript runs in the browser are not found.
- **No logins.** Pages behind a login or paywall are not crawled.
- **Some sites block automated checks.** These links are reported as `uncertain` rather than guessed as broken or fine, so check them by hand.
- **Anchors (`#section`) are not validated.** It checks that the page loads, not that the anchor exists on it.
- **One request per external link.** External sites are pinged, not crawled, so it cannot tell you about problems deeper inside them.

### FAQ

**How do I find broken links on my website?** Paste your homepage into **Website URLs** and click **Start**. When the run ends, open the **Problems overview** table or the HTML report to see every broken link and the page it is on.

**Does it find 404 errors and broken images?** Yes. Every link, `<img>`, script, stylesheet, iframe and media file is checked, and anything that returns 404, 410 or a 5xx error is marked `broken`.

**Why is a link "uncertain" and not "broken"?** Sites like LinkedIn, Instagram and some CDNs return 401/403/429/999 to automated requests even when the page works in a browser. We flag these separately so you don't waste time on false alarms.

**Does it check JavaScript-rendered links?** No. It reads the HTML your server sends, which is what search engines see first. That keeps it fast and cheap. Most sites, including WordPress, Shopify, Webflow and static sites, include their links in the HTML.

**Does it crawl external websites?** No. External links get one lightweight request each. Only your own site is crawled.

**Is it legal?** It only requests public URLs on the site you choose, plus the links that site points to. It collects no personal data and respects robots.txt when crawling. Only scan websites you own or have permission to audit.

**Can I run it on a schedule or from CI?** Yes. Use Apify Schedules for monthly audits, or call the Actor through the Apify API and fail your pipeline when `results.broken > 0` in `SUMMARY`.

**Something wrong or missing?** Open an issue on the Issues tab and we'll look into it.

### Related tools

- 🧪 **[SEO Audit Crawler](https://apify.com/goldish_fireplace/seo-audit-crawler)**: audit every page for missing titles, meta descriptions, H1s, canonicals, noindex, duplicate content and alt text, with a 0–100 SEO score per page. Run it together with Broken Link Checker for a complete technical SEO health check.

# Actor input Schema

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

One or more pages to start from, usually the home page (e.g. https://example.com). The Actor crawls pages on the same website and checks every link it finds.

## `maxPagesToCrawl` (type: `integer`):

How many pages of your website to open and scan for links. Each page's own status is also reported.

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

Safety cap on unique URLs (pages, links, images, scripts) to check. You are charged per unique link checked, so this also caps your cost.

## `outputMode` (type: `string`):

"Broken only" = 4xx/5xx and connection errors. "Problems" also includes redirects and links that block bots (401/403/429). "All" includes every working link too.

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

Also check links that point to other websites (one lightweight request each; external sites are never crawled).

## `checkAssets` (type: `boolean`):

Also check <img>, <script>, stylesheet, <iframe> and media URLs, not just <a> links.

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

When on, blog.example.com and shop.example.com are crawled as part of example.com.

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

Skip crawling pages that robots.txt disallows.

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

Regular expressions (or plain text) - matching URLs are neither crawled nor checked. Example: /tag/, ?replytocom=, linkedin.com

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

Maximum number of parallel requests overall.

## `maxConcurrencyPerHost` (type: `integer`):

Politeness limit for link checks against any single host.

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

How long to wait for a server before reporting a TIMEOUT error.

## `maxRetries` (type: `integer`):

Retries for network errors, 429 and 5xx responses before a link is reported.

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

Optional custom User-Agent header. Leave empty for a browser-like default.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://the-internet.herokuapp.com/"
    }
  ],
  "maxPagesToCrawl": 20,
  "maxLinksToCheck": 5000,
  "outputMode": "problems",
  "checkExternalLinks": true,
  "checkAssets": true,
  "includeSubdomains": false,
  "respectRobotsTxt": true,
  "excludeUrlPatterns": [],
  "maxConcurrency": 20,
  "maxConcurrencyPerHost": 8,
  "requestTimeoutSecs": 20,
  "maxRetries": 1
}
```

# Actor output Schema

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

No description

## `results` (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 = {
    "startUrls": [
        {
            "url": "https://the-internet.herokuapp.com/"
        }
    ],
    "maxPagesToCrawl": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("goldish_fireplace/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": [{ "url": "https://the-internet.herokuapp.com/" }],
    "maxPagesToCrawl": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("goldish_fireplace/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": [
    {
      "url": "https://the-internet.herokuapp.com/"
    }
  ],
  "maxPagesToCrawl": 20
}' |
apify call goldish_fireplace/broken-link-checker --silent --output-dataset

```

## MCP server setup

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