# Broken Link Checker: Find Dead Links on Your Pages (`ucchiii/broken-link-checker`) Actor

Broken link checker for the pages you list: every link (and optionally image) is checked with HEAD, then GET, and returned with HTTP status, broken yes/no, redirect target and anchor text. No crawling beyond your pages. Empty input = free demo.

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

## Pricing

from $0.35 / 1,000 checked 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

**Broken link checker for the pages you choose: every link on them, checked and returned as one row with its status.**

Paste the URLs of your pages, press Start, and get a table of every `<a href>` link on those pages (and, if you want, every image): the HTTP status code, whether the link is broken, where it redirects, the anchor text and the page it was found on. Only the pages you list are read: the Actor does not crawl your whole site, so a run is quick and you pay only for the links you asked about.

**Try it free:** leave the page list empty and press **Start**: a built-in sample page with a few real links (one of them a 404) is checked, not charged, and the rows carry `_demo: true`.

### What you get

| sourcePage | linkUrl | anchorText | statusCode | broken | redirectTarget | error |
|---|---|---|---|---|---|---|
| https://example.com/blog/ | https://example.com/pricing | Pricing | 200 | false | | |
| https://example.com/blog/ | https://example.com/old-guide | Read the guide | 404 | true | | |
| https://example.com/blog/ | http://partner.example.org/ | Our partner | 301 | false | https://partner.example.org/ | |
| https://example.com/blog/ | https://gone.example.net/ | Old tool | | true | | dns_failure |

- **Checks the way a browser would:** each link gets a `HEAD` request; when the server answers `HEAD` with 400 or more (many servers do this wrongly), the link is checked again with `GET` before it is called broken. Redirects are followed (up to 10) and the final target is shown.
- **One row per link:** with *List each link once* (default) a menu link that is on all 30 of your pages is one row, and `foundOnPages` lists the 30 pages. Turn it off for one row per page and link.
- **Polite:** at most 2 connections to the same host at a time, at least 0.25 s between requests to it, and each link is requested only once per run.

### How to use

1. **Pages to check**: full URLs, one per line (up to 100).
2. Optional: **Only links to the same site** (internal links only), **Also check images**, **Max links per page**, **Timeout**.
3. Press **Start**. Filter the dataset on `broken = true`, or download it as CSV or Excel.

```json
{
  "startUrls": ["https://example.com/", "https://example.com/blog/"],
  "sameSiteOnly": false,
  "includeImages": true,
  "dedupeLinks": true,
  "maxLinksPerPage": 500
}
```

### Output fields

- `sourcePage`: the page (as you gave it) where the link was found; `foundOnPages` and `occurrences` list every page with that link.
- `linkUrl`: the absolute link target, resolved against the page and its `<base href>`, without the `#fragment`.
- `anchorText`: the visible link text (or the alt text of an image inside the link). `linkType`: `link` or `image`. `internal`: same host as the page.
- `statusCode` (first answer), `finalStatusCode` (after redirects), `broken` (true when the final status is 400 or higher or the request failed), `redirectTarget`, `redirectCount`.
- `error`: `timeout`, `dns_failure`, `connection_error`, `ssl_error`, `too_many_redirects`, `redirect_loop`, `invalid_url` or `blocked_private_address`. `note`: a hint for 401/403/429/999 answers, which often mean the site refuses automated checks rather than a dead link.
- `responseTimeMs`, `checkedAt`. A **STATS** record lists every page read (or why it failed) and the run totals.

Skipped and never checked: `mailto:`, `tel:`, `javascript:` and `data:` links and links to a place on the same page (`#top`).

### Limits (cannot be raised by input)

- 100 pages per run, 1,000 links per page (so at most 100,000 links per run), 5 MB of HTML per page.
- Pages and links are requested only on public addresses: links to internal network addresses (localhost, private ranges, cloud metadata) are reported but not requested, and not charged.
- Pages that need a login, or that build their links with JavaScript after loading, show only the links in the delivered HTML.
- Set a maximum charge per run to cap spend: the Actor stops cleanly at that number of links.

### Pricing

Pay per event: **$0.0005 per checked link** ($0.50 per 1,000 links), whether the link works or is broken. Links that were not requested (internal addresses, invalid URLs), pages that failed to load and the demo are free.
Store discounts apply automatically: BRONZE −10%, SILVER −20%, GOLD −30%.
On the Apify Free plan, the $5 of monthly platform credit covers about 10,000 checked links a month.

### FAQ

**Does it crawl my whole website?** No. Only the pages you list are read. To check a whole site, give it the list of its pages (for example from your sitemap).

**Why is a link marked broken although it opens in my browser?** Some sites answer automated requests with 403, 429 or 999 (bot protection). These rows have a `note`; open the link to confirm.

**Are links checked more than once?** No. A link that appears on many of your pages is requested once per run.

**Can I check only the broken links later?** Filter the dataset on `broken = true` in Apify Console, or with the Filter Rows Actor.

If this Actor saved you time, a rating on the Store helps others find it.

### More tools from the same developer

- [Bulk URL Status Checker](https://apify.com/ucchiii/url-status-checker) — bulk HTTP status and redirect-chain checker for URL lists
- [Sitemap URL Extractor](https://apify.com/ucchiii/sitemap-urls) — list every URL of a website from its sitemaps
- [Website Technology Detector](https://apify.com/ucchiii/website-technology-detector) — tech stack of websites in bulk: CMS, frameworks, analytics and tag IDs, CDN, consent tools

# Actor input Schema

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

Full page URLs, one per line (up to 100 per run). Every <a href> link on these pages is checked; the Actor does not crawl further. Leave empty for a free demo on a built-in sample page (not charged).

## `sameSiteOnly` (type: `boolean`):

Check only internal links (same host as the page, www. ignored). Off = internal and external links.

## `includeImages` (type: `boolean`):

Also check the src of every <img> on the pages (rows with linkType 'image').

## `dedupeLinks` (type: `boolean`):

On: a link that appears on several of your pages is one row, with all pages in foundOnPages. Off: one row per page and link (a menu link on 50 pages = 50 rows). Each link is requested only once either way.

## `maxLinksPerPage` (type: `integer`):

Check at most this many links from each page, in page order. Between 1 and 1000. Empty = 1000.

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

How long to wait for each page or link. Between 1 and 60 (default 15). A link that does not answer in time is reported as broken with error 'timeout'.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.python.org/"
  ],
  "sameSiteOnly": false,
  "includeImages": false,
  "dedupeLinks": true,
  "maxLinksPerPage": 25,
  "timeoutSecs": 15
}
```

# Actor output Schema

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

One item per link: source page, link URL, anchor text, status code, broken yes/no, redirect target, error.

## `stats` (type: `string`):

Record STATS: each page read (or why it failed), links checked, broken links, links charged and whether the run stopped early.

# 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://www.python.org/"
    ],
    "maxLinksPerPage": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("ucchiii/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://www.python.org/"],
    "maxLinksPerPage": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("ucchiii/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://www.python.org/"
  ],
  "maxLinksPerPage": 25
}' |
apify call ucchiii/broken-link-checker --silent --output-dataset

```

## MCP server setup

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