# Broken Link Checker: 404s, Redirects and Dead Links (`pandadad0214/broken-link-checker`) Actor

Use this to find broken links on a website: crawl starting pages (optional same-host follow), check each unique http(s) URL, and get status, final URL, redirect flag and a broken flag. Excel report of every checked link.

- **URL**: https://apify.com/pandadad0214/broken-link-checker.md
- **Developed by:** [Panda Federation](https://apify.com/pandadad0214) (community)
- **Categories:** Developer tools, Automation, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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: 404s, Redirects and Dead Links

**Use this to find broken links on a website: crawl starting pages (optional same-host follow), check each unique http(s) URL, and get status, final URL, a redirect flag and a broken flag.** Results come as an **Excel report** (every checked link, plus a sheet of only the broken ones) and CSV. Apify's free plan includes monthly platform credit you can use to try it.

This is **not a Google search scraper**. It does not search the web for you. It checks the pages you provide and the links found on those pages.

- 🔗 **Start from one page or a checklist of URLs** and optionally follow more HTML pages on the same host
- 🧭 **Unique http(s) links only** — `mailto:`, `tel:`, `javascript:`, `data:` and empty/#-only fragments are skipped
- 🚩 **Broken flag** for HTTP 400+ (404, 410, 500…) and a **redirected** flag when the final URL differs (trailing slash and `#fragment` ignored)
- 🌍 **Outbound links** checked by default; turn that off to stay on-site
- 🛡️ **Private and internal addresses are blocked** (loopback, link-local, RFC1918) and never billed
- 💸 **$0.001 per link that returns an HTTP status** (including 4xx/5xx). Timeouts, DNS failures, robots-skipped crawl targets and blocked private IPs are free

### Who it's for

- **SEO specialists and content teams:** catch 404s and redirect chains before they hurt rankings or crawl budget
- **Site owners and webmasters:** a regular health check of navigation, footer, blog and resource links
- **Agencies and freelancers:** a client-ready Excel of every checked URL, with a separate sheet of just the broken ones
- **AI agents:** answer "which links on this page or site are dead?" without browsing by hand

### How to use it

1. Click **Try for free**.
2. Add **start URLs**, one per line (a homepage, a sitemap-style list, or a few key pages).
3. Set **Pages to crawl per host** (`1` = only those start pages; higher values follow more same-host HTML pages) and **Maximum links to check** (this caps what you pay).
4. Optional: turn off **Check outbound (external) links** to stay on-site, or change the per-request timeout.
5. Click **Start**. Download **LINKS_XLSX** or **LINKS_CSV** from **Storage → Key-value store**. Open the **Broken** sheet for just the 404s and server errors.

#### Input example

```json
{
  "startUrls": ["https://example.com"],
  "maxPages": 1,
  "maxLinks": 50,
  "checkExternal": true
}
```

### Task examples

- **Quick check of one page:** `{"startUrls": ["https://yoursite.com"], "maxPages": 1, "maxLinks": 100}`
- **Small same-host crawl:** `{"startUrls": ["https://yoursite.com"], "maxPages": 10, "maxLinks": 200}`
- **On-site only (no outbound hosts):** `{"startUrls": ["https://yoursite.com"], "checkExternal": false, "maxPages": 20, "maxLinks": 400}`
- **Checklist of known URLs:** pass many start URLs with `"maxPages": 1` — each URL is checked, and links on those pages are collected up to your cap
- **Weekly agency audit:** schedule the Actor weekly with `maxPages` 25 and `maxLinks` 500, then download the Broken sheet

### Output example

One row per unique URL. A real row from a platform test run against `https://example.com` (`maxPages` 1). That page currently has no outbound `<a href>` links, so the start URL itself is checked:

```json
{
  "originalUrl": "https://example.com/",
  "finalUrl": "https://example.com/",
  "statusCode": 200,
  "broken": false,
  "redirected": false,
  "external": false,
  "contentType": "text/html; charset=utf-8",
  "sourcePage": "https://example.com/",
  "host": "example.com",
  "error": null,
  "agentSummary": "200 https://example.com/ OK"
}
```

A real outbound-link row from a platform test of `https://apify.com` (`maxPages` 1, `maxLinks` 20; 20 links checked, 3 external):

```json
{
  "originalUrl": "https://console.apify.com/sign-up",
  "finalUrl": "https://console.apify.com/sign-up",
  "statusCode": 200,
  "broken": false,
  "redirected": false,
  "external": true,
  "contentType": "text/html",
  "sourcePage": "https://apify.com/",
  "host": "console.apify.com",
  "error": null,
  "agentSummary": "200 https://console.apify.com/sign-up (from https://apify.com/) OK"
}
```

Typical broken and redirect summaries look like:

- `404 https://example.com/missing (from https://example.com/about) → not found`
- `200 https://example.com/docs OK`
- `200 https://old.example.com/pricing (from https://example.com/) → https://example.com/pricing (redirect)`

#### Fields

| Field | What it is |
|---|---|
| `originalUrl`, `finalUrl` | The URL as found, and where it ended after redirects (hop cap 5) |
| `statusCode` | HTTP status of the final response, or `null` when no status was received |
| `broken` | `true` when `statusCode` is 400 or higher |
| `redirected` | `true` when the final URL differs from the original (trailing slash and fragment ignored) |
| `external` | `true` when the host is not one of your start URL hosts |
| `contentType`, `sourcePage`, `host`, `error` | Response type, page where the link was found, hostname, and any fetch error |
| `agentSummary` | One plain-English line per URL |

#### Files in the key-value store

| Key | What it is |
|---|---|
| `LINKS_XLSX` | Excel workbook: **Links** (every checked URL), **Broken** (only `broken == true`), **Run log**, **Read me** |
| `LINKS_CSV` | Links table as CSV |
| `SUMMARY` | JSON: counts, top status codes, `broken` and `failed` lists, a one-line `takeaway` and `nextStep` |

### For AI agents

Use this Actor to answer "which links on this page or site are broken?" or to scan a checklist of URLs. Call it with Apify's MCP server (`https://mcp.apify.com`, tool `call-actor`, Actor `pandadad0214/broken-link-checker`) or the API (`POST https://api.apify.com/v2/acts/pandadad0214~broken-link-checker/run-sync-get-dataset-items`).

Minimal input:

```json
{"startUrls": ["https://example.com"], "maxPages": 1, "maxLinks": 50}
```

Each dataset item includes:

- `agentSummary`: e.g. `404 https://example.com/missing (from https://example.com/about) → not found` or `200 https://example.com/ OK`
- `statusCode`, `broken`, `redirected`, `finalUrl`, `sourcePage`

The run's `SUMMARY` record has a one-line `takeaway` (how many checked, how many broken, top status codes, how many external) plus `nextSteps`, so you can answer without parsing every row.

### Pricing

**$0.001 per unique link that returned an HTTP status** (pay per event), including 4xx and 5xx, plus a tiny Actor-start fee of $0.00005. Excel, CSV and the summary are included. **Timeouts, DNS failures, robots-skipped crawl targets and blocked private or internal IPs are not charged.** Runs that receive no HTTP statuses charge no `link-checked` events. Set **Maximum links to check** or Apify's *maximum charge per run* to cap any run; the Actor stops at your limit.

**Apify paid-plan discounts:** Bronze members pay $0.0009, Silver $0.0008 and Gold, Platinum and Diamond $0.0007 per checked link; the discount applies automatically. Apify's free plan includes monthly credit to try it.

A 200-link audit costs about $0.20 on the free tier (plus the tiny start fee).

### FAQ

**What counts as broken?** An HTTP status of 400 or higher (404 Not Found, 410 Gone, 403 Forbidden, 500 server error, and so on). A redirect that ends on a 2xx page is not broken; it is flagged `redirected` instead.

**Why are some timeouts free?** You are charged only when the check received an HTTP status. If the host never answers, DNS fails, the address is private/internal, or a page is skipped as a crawl source because of robots.txt, that URL is recorded in the log/SUMMARY (and listed with an `error`) but not billed.

**Do you respect robots.txt?** Yes when *crawling* extra same-host pages as sources of more links: `/robots.txt` is fetched once per host, and disallowed pages are not used as crawl sources. A link you asked us to verify (or a link we already found) can still be checked with HEAD/GET.

**Will it work on login-walled sites?** No. Only the public HTML the server returns is read. Pages behind a login may redirect to a sign-in screen or look empty; that is not treated as a broken link unless the final status is 400+.

**Is this a Google search scraper?** No. It never queries Google or any search engine. It only fetches the start URLs you provide and the http(s) links found on those pages.

**How are redirects handled?** Each hop is validated (no following into loopback, link-local or private RFC1918 addresses) with a cap of 5 hops. The final URL and status are recorded.

**Can it check `mailto:` or `javascript:` links?** Those schemes, plus `tel:`, `data:` and empty or `#`-only fragments, are skipped. Only `http` and `https` URLs are checked.

### More Panda Federation tools

- [Website Change Monitor](https://apify.com/pandadad0214/website-change-monitor): watch pages for content, price and keyword changes
- [AI Search & SEO Audit](https://apify.com/pandadad0214/ai-search-seo-audit): check a site for SEO and AI-search readiness
- [Email Domain Security Checker](https://apify.com/pandadad0214/email-domain-security-checker): SPF, DKIM and DMARC grades for any list of domains
- Full catalog: [Panda Federation on Apify](https://apify.com/pandadad0214) and [Panda Federation Tools on GitHub](https://github.com/pandadad0214/panda-federation-tools)

*Built by Panda Federation. Questions or a custom version? Open an issue on the Actor's Issues tab.*

### Changelog

- **2026-11-08:** First release (0.1.0): crawl start URLs with optional same-host follow (robots.txt for crawl sources), unique http(s) link checks (HEAD then GET fallback), SSRF protection for loopback / link-local / RFC1918 / internal hosts, redirect walking with a hop cap, Excel (`LINKS_XLSX` with a Broken sheet) and CSV, `agentSummary` and SUMMARY `takeaway`, pay-per-event pricing (`link-checked`) with Bronze/Silver/Gold discounts. Timeouts, DNS failures, robots-skipped crawl targets and blocked private IPs are not charged.

# Actor input Schema

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

Pages to start from. The Actor fetches each page, extracts http(s) links, and checks them. Leave empty for a short demo on example.com. One URL per line.

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

How many HTML pages on the same host as each start URL to fetch for extracting more links. 1 = only the start URL(s). Further same-host pages are queued only when this is greater than 1. robots.txt is respected for crawl sources.

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

Maximum unique http(s) URLs to check. This is what you are charged for. Hard max 2,000. (maxResults is accepted as an alias.)

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

When on, links to other hosts are checked too. Turn off to check only same-host links.

## `timeoutSecs` (type: `integer`):

How long to wait for each request. Timed-out URLs are recorded but not charged.

## Actor input object example

```json
{
  "startUrls": [
    "https://example.com",
    "https://your-site.com/about"
  ],
  "maxPages": 5,
  "maxLinks": 200,
  "checkExternal": true,
  "timeoutSecs": 15
}
```

# Actor output Schema

## `table` (type: `string`):

No description

## `xlsx` (type: `string`):

No description

## `csv` (type: `string`):

No description

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

Run-level summary record. Agent-friendly keys: `takeaway` (one-line result), counts, `broken`, `failed`, `nextStep` and `nextSteps`.

# 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://example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pandadad0214/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://example.com"] }

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

```

## MCP server setup

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