# Broken Link Checker (`cyber1nj3ct0r/broken-link-checker`) Actor

Find broken links and images on any website. Reads the site's pages, checks every link and image on them, and lists each broken one (404, server error, dead domain, timeout) with the pages it appears on. Also flags redirects and links that sites refuse to check.

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

## Pricing

from $0.60 / 1,000 links

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

### What does Broken Link Checker do?

Broken Link Checker finds the links on your website that no longer work. Give it the address of a site and it reads the site's pages, collects every link and image on them, and checks each one. You get a list of the broken links (pages not found, server errors, domains that no longer exist, timeouts) together with the **exact pages they appear on**, so you know what to fix and where.

- Reads the pages of your site by following its own links, the way a search engine does
- Checks links to your own pages, links to other websites, and images (stylesheets, scripts, video and embedded frames on request)
- Checks each different link once, however many pages it is on, and lists up to 20 of those pages
- Separates truly broken links from links that only redirect, and from sites that refuse automated checks
- Respects `robots.txt` and spaces out its requests, so it is safe to run on live sites
- Saves a readable report of the broken links, page by page, next to the full results

### What do you get?

For every link checked:

| Field | Example |
|---|---|
| Link | `https://www.example.com/old-offer` |
| Result | `broken` (or `ok`, `redirected`, `blocked`, `skipped`) |
| HTTP status | `404` |
| Problem | `Page not found (HTTP 404)` |
| Type | `link` or `image` |
| Same site | `true` |
| Found on | the pages that contain the link, and how many |
| Link text | the clickable text, or the image's alt text |

Three more records come with each run: a **report** page you can open in your browser, the **broken links** alone as JSON, and a **summary** with the counts.

### How to find broken links on a website

1. Enter the website under **Websites**, for example `https://www.example.com`.
2. Set **Maximum pages to read per website**. Every page of a small site is covered by the default of 200.
3. Press **Start**.
4. When the run ends, open **Broken links report** on the Output tab, or export all results as Excel, CSV or JSON.

### How much does it cost to check links?

You pay for links checked, not for time: **$0.80 per 1,000 links**. Apify subscribers on the Silver plan pay $0.70 and on the Gold plan $0.60 per 1,000 links. Each run also has a start fee of $0.00005. Reading the pages of your site is free; only the different links checked count, so a menu link that appears on every page is paid once.

| Links checked | Cost |
|---|---|
| 100 | $0.08 |
| 1,000 | $0.80 |
| 10,000 | $8.00 |
| 100,000 | $80.00 |

Apify's free plan includes $5 of credit each month, which covers about 6,000 links. Set **Maximum links to check**, or a maximum cost for the run in Apify Console, and the tool stops there and keeps what it has checked.

### Input

The only required field is **Websites**. The other options:

- **Maximum pages to read per website**: how far the tool reads into each site.
- **Maximum links to check**: caps the run and its cost.
- **Check links to other websites**: on by default; turn it off to check only your own pages and files.
- **What to check**: links and images by default; add stylesheets, scripts, video and audio, or embedded frames.
- **Only read pages matching** and **Skip pages matching**: limit which pages are read, for example only `/blog/`. Links found on the pages read are all checked.
- **Time limit per request** and **User-Agent** under More options.

### Output example

```json
{
    "url": "https://www.example.com/summer-sale",
    "result": "broken",
    "broken": true,
    "status": 404,
    "reason": "Page not found (HTTP 404)",
    "finalUrl": null,
    "redirectCount": 0,
    "type": "link",
    "internal": true,
    "linkText": "Summer sale",
    "nofollow": false,
    "foundOnCount": 3,
    "foundOn": ["https://www.example.com/", "https://www.example.com/shop", "https://www.example.com/blog/june"],
    "responseTimeMs": 182,
    "contentType": "text/html",
    "site": "https://www.example.com",
    "error": null
}
```

### What the results mean

- **ok**: the link works.
- **redirected**: the link works but goes through a redirect first. Pointing it straight at the final address (shown in `finalUrl`) saves visitors a step.
- **broken**: visitors get an error: page not found (404) or removed (410), a server error (5xx), a domain that does not exist, a server that does not answer, a certificate problem, or a link address that cannot be read at all.
- **blocked**: the other site refused the automated check (for example HTTP 403 or 429). Many large sites do this; the link usually works in a browser. These are listed separately so they do not hide the real problems.
- **skipped**: not checked because the site's `robots.txt` asks programs not to, or because it points to a private network address.

### What can you use it for?

- Fix broken links before visitors and search engines find them
- Check a site after a redesign, a move to a new domain or a change of addresses
- Find links to partner sites that have closed or moved
- Find missing images
- Run it on a schedule in Apify Console and get the report every week

### Use it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/cyber1nj3ct0r~broken-link-checker/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrls": [{"url": "https://www.example.com"}], "maxPages": 500}'
```

The same works from Python, JavaScript, Make, Zapier and n8n through Apify's integrations.

### FAQ

#### Why are some links "blocked" and not "broken"?

Some sites, often the large ones, answer automated requests with "forbidden" or "too many requests" even when the page exists. The tool reports what the site answered and does not try to get round it, so those links are marked **blocked**. Open them in a browser to be sure.

#### Does it read pages that are built by JavaScript?

It reads the HTML the server sends. Links that only appear after JavaScript runs in a browser are not seen. Most sites, including sites built with WordPress, Shopify and most site builders, send their links in the HTML.

#### Why was a page of my site not read?

Either the page limit was reached, your `robots.txt` asks programs not to read it, or it is not linked from the pages that were read. Raise the page limit, check `robots.txt`, or start from another page.

#### A site blocks the tool. What can I do?

The tool identifies itself honestly in its User-Agent and does not try to get round blocks. If it is your own site, allow the User-Agent in your firewall, or set your own under **More options**.

#### Is it allowed to check links?

The tool requests public pages the way a visitor's browser would, once per link, and follows `robots.txt`. You are responsible for checking only sites you are allowed to and for how you use the results.

#### Something does not work

Open an issue on the **Issues** tab with the website you tried. Every report is read and answered.

# Actor input Schema

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

The page to start from on each website, usually the home page, for example <code>https://www.example.com</code>. The tool follows the links from there to other pages of the same site.

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

How many pages of each website to read for links. Reading pages is not charged; checking the links found is. Large sites need a higher number to have every page covered.

## `maxLinks` (type: `integer`):

Stop after checking this many different links. You pay per link checked, so this also caps the cost of the run.

## `checkExternal` (type: `boolean`):

Also check links that lead to other websites. Turn off to check only the website's own pages and files.

## `linkTypes` (type: `array`):

Which kinds of references on the pages to check. Links and images are checked by default.

## `includePatterns` (type: `array`):

Read a page for links only if its address matches at least one of these. Plain text matches anywhere in the address and <code>\*</code> stands for any characters, for example <code>/blog/</code>. For a regular expression, wrap it in slashes. Links found on the pages read are all checked.

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

Do not read pages whose address matches any of these, for example <code>/tag/</code> or <code>?page=</code>. Links to them are still checked.

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

How long to wait for one page or link before counting it as not answering.

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

The name the tool sends with each request. Leave empty to use the tool's own name, which tells site owners what is visiting. Set your own only for sites you run or have permission to test.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://crawlee.dev"
    }
  ],
  "maxPages": 10,
  "maxLinks": 150,
  "checkExternal": true,
  "linkTypes": [
    "link",
    "image"
  ],
  "requestTimeoutSecs": 20
}
```

# Actor output Schema

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

A web page listing the broken links by the page they are on, and the links that sites refused to check.

## `links` (type: `string`):

One record per link checked, with its result, HTTP status and the pages it appears on.

## `brokenLinks` (type: `string`):

Only the broken links and the links that sites refused to check, as JSON.

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

Counts for the run: pages read, links found and checked, broken, redirected, refused and skipped links.

# 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"
        }
    ],
    "maxPages": 10,
    "maxLinks": 150
};

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

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

```

## MCP server setup

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