# Broken Link Checker (`jancurn/find-broken-links`) Actor

Crawls a website and finds broken links. Unlike other similar SEO analysis tools, the actor also reports broken URL #fragments. The results are stored in a JSON and HTML report.

- **URL**: https://apify.com/jancurn/find-broken-links.md
- **Developed by:** [Jan Čurn](https://apify.com/jancurn) (community)
- **Categories:** SEO tools, Open source
- **Stats:** 800 total users, 3 monthly users, 100.0% runs succeeded, 17 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### What does Broken Links Checker do?

Our Broken Links Checker is an easy-to-use SEO tool to help you keep your UX and SEO score healthy, improve your website ranking, and prevent link rot.

### How does it check for broken links?

Broken Link Checker can crawl any website and do all of the following:

- generate a report containing an inspection of **all links on the website** or **only broken links**
- scan **one or multiple websites** simultaneously
- easily check **domain and subdomains**
- identify **broken link fragments**
- give you a neat **report in your email inbox** once the SEO inspection is complete
- initiate a **SEO check manually or automatically** thanks to our powerful scheduling system

### How much does it cost to run Broken Links Checker?

Using our basic plan, the scraper's run will cost you around  **USD 0.25 in Apify platform credits per 1,000 scraped results**. For more details about the plans we offer, platform credits and usage, see the  [platform pricing page](https://apify.com/pricing/actors).

### How to start Broken Links Checker

Broken Links Checker is highly adaptable to your SEO requests and can scan your web pages quickly and regularly. To check a  website for bad links, repeat the following steps:

1. Click on *Try for free*.
2. Add one or more website URLs to start the audit from.
3. Enable the *Save only broken links* button.
4. *Add your email address* to receive the full SEO report in your inbox.
5. Click *Run* and wait for the data to be collected.
6. Optional step: *Schedule* the tool to check the links automatically every month, week or specific time during the day.

For a more detailed instruction with visual aids of how to set up a broken links checker and why, see our [step-by-step tutorial](https://blog.apify.com/step-by-step-guide-to-using-broken-links-checker/) on checking for broken links.

### What's happening under the hood?

Broken Links Checker will start the link check at a given URL and will crawl all linked pages under that website. So for example, if the crawler starts at `https://www.example.com/something`, then it will also crawl linked pages such as:

```
https://www.example.com/something/index.html
https://www.example.com/something/else
https://www.example.com/something/even/more/deeper/file.html
```

On every checked page, the crawler will also analyze whether links to other pages are working or not. For example, if the page contains a link to `https://www.example.com/another/page#anchor`, the actor will open the inspected page `https://www.example.com/another/page`, check whether it loads correctly and then it also check if it contains the `#anchor`.

### Input options

If this actor is run on the [Apify platform](https://console.apify.com/), our user-friendly UI will help you configure all the necessary and optional parameters of this scraper before running it. Our Broken Links Checker recognizes the following input fields:

**Website URL**
The initial URL to start the broken links inspection from.

**Max pages**
Use this field to set the maximum number of pages to be checked. If left empty, the number will be unlimited.

**Notification emails**
Add the email address to receive a notification after the crawler discovers all broken links.

**Save only broken links**
If set to `true`, you'll get only the broken links in the report. If set to `false`, the crawler will include into the report both broken and healthy links (not a CSV friendly option).

**Crawl subdomains**
If set to `true`, the crawler will search broken links not only on the main page but also in deeper subdomains.

For more technical details on the input, head over to the [Input tab](https://apify.com/jancurn/find-broken-links/input-schema).

#### Input example

Here's an input example for checking the Apify Blog for bad links. We've enabled the crawler to check subdomains as well but limited the inspection to 1,000 pages.

```json
    {
      "baseUrl": "https://blog.apify.com/",
      "maxPages": 1000,
      "notificationEmails": [
        "your.email@apify.com"
      ],
      "saveOnlyBrokenLinks": true,
      "crawlSubdomains": true
    }
```

### Output

Once the links checker finishes the crawl, it will save a report of the broken links into your **key-value store**. You will find reports in two formats there:

- `OUTPUT`  contains a machine-readable JSON report
- `OUTPUT.html` contains an easy-to-read HTML report

#### Output example as JSON

Here's an example of dataset of a successful Broken Links Checker run. The error message is included in the report and can be found at the bottom of the example.

```json
[
  {
    "url": "https://blog.apify.com/",
    "title": "Apify Blog: Web scraping and automation stories",
    "links": [
      {
        "url": "https://apify.com/",
        "normalizedUrl": "https://apify.com/",
        "httpStatus": 200,
        "fragment": "",
        "fragmentValid": true,
        "crawled": true
      },
      {
        "url": "https://apify.com/about",
        "normalizedUrl": "https://apify.com/about",
        "httpStatus": 200,
        "fragment": "",
        "fragmentValid": true,
        "crawled": true
      },
      {
        "url": "https://apify.com/jobs",
        "normalizedUrl": "https://apify.com/jobs",
        "httpStatus": 200,
        "fragment": "",
        "fragmentValid": true,
        "crawled": true
      },
      {
        "url": "https://apify.com/web-scraping",
        "normalizedUrl": "https://apify.com/web-scraping",
        "httpStatus": null,
        "errorMessage": "Error: Navigation timed out after 120 seconds.\n    at handleRequestTimeout (/home/myuser/node_modules/apify/build/crawlers/crawler_utils.js:19:11)\n    at PuppeteerCrawler._handleNavigationTimeout (/home/myuser/node_modules/apify/build/crawlers/browser_crawler.js:418:54)\n    at PuppeteerCrawler._handleNavigation (/home/myuser/node_modules/apify/build/crawlers/browser_crawler.js:401:18)\n    at async PuppeteerCrawler._handleRequestFunction (/home/myuser/node_modules/apify/build/crawlers/browser_crawler.js:343:13)\n    at async wrap (/home/myuser/node_modules/@apify/timeout/index.js:73:27)",
        "fragment": "",
        "fragmentValid": true,
        "crawled": true
      },
...
```

### Other SEO audit tools

You can find other free SEO tools in the [Related actors](https://apify.com/jancurn/find-broken-links/related-actors) tab: Web Page Analyzer, SEO Audit Tool, Google Search Results Scraper. You should also check out [5 powerful scrapers to add to your SEO tool kit](https://blog.apify.com/5-powerful-scrapers-to-add-to-your-seo-toolkit/).

# Actor input Schema

## `baseUrl` (type: `string`):

The URL to crawl. Only URLs pages in the sub-directory will be probed.

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

The maximum number of pages that will be crawled. If empty, the number is not limited.

## `notificationEmails` (type: `array`):

Add email addresses to receive notifications whenever the actor discovers broken links.

## `saveOnlyBrokenLinks` (type: `boolean`):

If false, all checked links and all information about them will be saved (not CSV friendly).

## `crawlSubdomains` (type: `boolean`):

If true, actor will crawl all links recursively.

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

The maximum number of pages from the target website to crawl in parallel. You can use this number to reduce the load on the target web server.

## `proxyConfiguration` (type: `object`):

This is required if you want to use Apify Proxy.

## Actor input object example

```json
{
  "baseUrl": "https://www.example.com/",
  "maxPages": 1000,
  "notificationEmails": [
    "email@example.com"
  ],
  "saveOnlyBrokenLinks": false,
  "crawlSubdomains": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "baseUrl": "https://www.example.com/",
    "maxPages": 1000,
    "notificationEmails": [
        "email@example.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("jancurn/find-broken-links").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 = {
    "baseUrl": "https://www.example.com/",
    "maxPages": 1000,
    "notificationEmails": ["email@example.com"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("jancurn/find-broken-links").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 '{
  "baseUrl": "https://www.example.com/",
  "maxPages": 1000,
  "notificationEmails": [
    "email@example.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call jancurn/find-broken-links --silent --output-dataset

```

## MCP server setup

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

```

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/zkfWQi3yZWcjqhWhi/builds/QfvBKsr8bkkWjJrX5/openapi.json
