# Broken Link Checker for Technical SEO Audits (`khadinakbar/broken-link-checker`) Actor

Check links from a website crawl, URL list, or sitemap. Receive HTTP status, redirect chains, response timing, source pages, anchor text, and diagnostic classes for SEO quality assurance and site migrations.

- **URL**: https://apify.com/khadinakbar/broken-link-checker.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** SEO tools, Developer tools, MCP servers
- **Stats:** 38 total users, 14 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 link checkeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## 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

## Broken Link Checker for Technical SEO Audits

Turn a website crawl, direct URL list, or sitemap into a source-linked link health dataset for SEO teams, content operators, migration leads, developers, and AI agents. Each checked URL can include HTTP status, final URL, redirect path, response time, diagnostic classification, source page, anchor text, link type, and collection time, with downloadable dataset and report outputs.

### Best fit for this Actor

- Choose crawl mode when the starting point is a website and the goal is to discover and verify links within a controlled page scope.
- Choose list mode when URLs already exist in a spreadsheet, deployment manifest, or quality-assurance queue.
- Choose sitemap mode when the sitemap is the canonical inventory for a technical SEO review.
- After prioritizing important pages, pair this Actor with the [Website Uptime Monitor](https://apify.com/khadinakbar/website-uptime-monitor) for recurring HTTP, SSL, content, and response-time observations.

### From a site migration to a repair queue

A migration lead starts with the production sitemap and requests a complete checked-link inventory. The Actor follows redirects, records timing, and associates discovered URLs with their source pages and anchor text. The lead then groups records by classification, routes high-value pages into a repair queue, and carries `sourceUrl`, `allSources`, and `redirectChain` into each ticket so the team can act without repeating discovery work.

### Quick start input

```json
{
  "mode": "sitemap",
  "startUrl": "https://example.com/sitemap.xml",
  "maxLinksToCheck": 500,
  "onlyReportBroken": false,
  "checkExternalLinks": true
}
```

For a curated inventory, set `mode` to `list` and provide full URLs through `urls`. For site discovery, set `mode` to `crawl` and use `maxPages` to define the page scope.

### Input reference

| Field | Type | What it controls |
|---|---|---|
| `mode` | string | URL sourcing through crawl, list, or sitemap workflow. |
| `startUrl` | string | Website start page or sitemap URL. |
| `urls` | array | Full URLs for direct list verification. |
| `maxPages` | integer | Page-discovery cap in crawl mode. |
| `maxLinksToCheck` | integer | Stable cap on unique URL checks and primary event charges. |
| `checkExternalLinks` | boolean | Includes links that point to other domains. |
| `checkAssets` | boolean | Includes images, scripts, stylesheets, and frames. |
| `onlyReportBroken` | boolean | Chooses a focused repair dataset or a complete checked-link inventory. |
| `slowThresholdMs` | integer | Response-time threshold for the `slow` classification. |
| `requestTimeoutMs`, `maxConcurrency` | integer | Request timing and parallelism controls. |

### What data you receive

One dataset row represents one normalized URL check. The Actor also creates a summary and an HTML report for review and sharing.

| Field group | Useful fields |
|---|---|
| URL outcome | `url`, `finalUrl`, `status`, `statusText`, `classification`, `isBroken` |
| Discovery context | `sourceUrl`, `anchorText`, `linkType`, `allSources` |
| Redirects | `hops`, `redirectChain` |
| Request evidence | `method`, `durationMs`, `checkedAt` |
| Diagnostic detail | Structured transport context when a request yields a diagnostic |

```json
{
  "url": "https://example.com/old-page",
  "finalUrl": "https://example.com/new-page",
  "status": 200,
  "statusText": "OK",
  "classification": "redirect_chain",
  "isBroken": false,
  "sourceUrl": "https://example.com/resources",
  "anchorText": "Product guide",
  "linkType": "a",
  "method": "GET",
  "hops": 2,
  "redirectChain": [
    { "status": 301, "from": "https://example.com/old-page", "to": "https://example.com/archive" },
    { "status": 302, "from": "https://example.com/archive", "to": "https://example.com/new-page" }
  ],
  "durationMs": 420,
  "checkedAt": "2026-06-02T09:00:00.000Z"
}
```

### Use through the API

```bash
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~broken-link-checker/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "sitemap",
    "startUrl": "https://example.com/sitemap.xml",
    "maxLinksToCheck": 500,
    "onlyReportBroken": false
  }'
```

Read the default dataset for URL-level records. The default key-value store also exposes the summary and HTML report through the Actor output links.

### Use with AI agents through Apify MCP

> Audit this sitemap for link health. Return URL, final URL, HTTP status, classification, redirect path, response time, source page, and anchor text. Read the dataset and summary after completion, group the repair queue by classification and source page, preserve provenance, and keep the check scope within the supplied link cap.

For predictable agent behavior, state the sourcing mode, include the canonical start point, request a complete inventory when comparison matters, and ask the agent to cite `sourceUrl` beside each recommended action. `maxLinksToCheck` makes scope and cost explicit.

### Connect the workflow

Use the [Website Uptime Monitor](https://apify.com/khadinakbar/website-uptime-monitor) after a technical SEO audit when selected URLs need recurring health observations. Pass the priority `url` values into its `startUrls` input for HTTP, SSL, content, and response-time monitoring.

### Pricing

This Actor uses Pay per event plus Apify platform usage. Open the live Pricing tab for current event details, and use `maxPages`, `maxLinksToCheck`, and Apify run cost controls to keep the audit aligned with your budget.

### Best results

- Use sitemap mode when the sitemap is a maintained source of canonical pages.
- Use crawl mode with a focused `maxPages` value when source-page and anchor discovery are central to the audit.
- Keep `onlyReportBroken` disabled for before-and-after migration comparisons that need the complete inventory.
- Preserve `sourceUrl`, `allSources`, `redirectChain`, and `checkedAt` in downstream repair and verification workflows.

### Builder's note

I built the output around both the checked URL and the pages that reference it because I found that a status alone rarely tells a content team where to make the change. Capturing anchor text, link type, multiple sources, redirect history, and timing turns a raw HTTP observation into a repair-ready record.

### Responsible use

Check websites and URLs you are authorized to assess, use considerate concurrency, and follow applicable laws, site terms, and your organization's security and quality-assurance policies.

# Actor input Schema

## `mode` (type: `string`):

How URLs are sourced. 'crawl' starts at startUrl and crawls the site to maxPages, extracting and verifying every link found. 'list' verifies only the URLs in the urls array (no crawling). 'sitemap' fetches sitemap.xml from startUrl and verifies every URL found. Default: 'crawl'. Choose 'list' for outreach link audits and 'sitemap' for full-site URL coverage without crawling overhead.

## `startUrl` (type: `string`):

The URL to start crawling from (mode='crawl') or the sitemap.xml URL (mode='sitemap'). Must include the protocol — example: 'https://example.com/' or 'https://example.com/sitemap.xml'. Crawler stays within the same registrable domain by default. Ignored when mode='list'.

## `urls` (type: `array`):

List of URLs to check directly without crawling. Used only when mode='list'. Each entry must be a full URL including protocol — example: \['https://example.com/page1', 'https://example.com/missing']. Up to 5000 URLs per run. Ignored when mode='crawl' or 'sitemap'.

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

Maximum number of internal pages to crawl (mode='crawl' only). Higher values find more broken links but take longer. Default: 50. Set to a small value (5-10) for quick audits; 200-500 for thorough site reviews.

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

Maximum number of unique links to verify per run. Caps cost on link-heavy sites. Default: 500. Each verified link triggers a 'link-checked' charge of $0.001.

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

Verify links pointing to other domains, not just internal links. Default: true. Set to false to focus only on same-domain links and reduce cost (typically halves the link count).

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

Also verify <img>, <script>, <link>, and <iframe> resource URLs in addition to <a> hyperlinks. Default: false. Enable for full content audits; disable for hyperlink-only checks (faster and cheaper).

## `onlyReportBroken` (type: `boolean`):

If true, dataset contains only broken/failing links (status >= 400, timeouts, errors). If false, every checked link is recorded with its status. Default: true. Set to false to get a complete link inventory with response times.

## `slowThresholdMs` (type: `integer`):

Response time in milliseconds above which a link is classified as 'slow'. Default: 5000. Slow links are reported (status 200 + slow flag) so you can find performance issues without classifying them as broken.

## `requestTimeoutMs` (type: `integer`):

Maximum time to wait for a response before classifying a link as 'timeout'. Default: 15000 (15s). Increase for slow targets; decrease to fail fast on dead sites.

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

Number of concurrent HTTP requests during link verification. Default: 20. Higher values are faster but may trigger rate limits on small sites. Reduce to 5-10 for fragile or rate-limited targets.

## `maxRedirects` (type: `integer`):

Maximum redirect hops to follow per URL before classifying as 'redirect\_loop'. Default: 10. Redirect chains longer than 2 hops are flagged as 'redirect\_chain' (still a 200 but worth reviewing).

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

Custom User-Agent string sent on every request. Default identifies as ApifyBrokenLinkChecker. Use a browser UA if a target site blocks bots; leave default for transparency and lower block rate.

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

Proxy settings for outbound requests. Default: no proxy (direct, fastest). Enable Apify Proxy only if a target site blocks your IP — most link checks work fine without proxies. Use residential group only when datacenter is blocked.

## Actor input object example

```json
{
  "mode": "crawl",
  "startUrl": "https://example.com/",
  "urls": [
    "https://example.com/",
    "https://example.com/this-page-does-not-exist",
    "https://httpstat.us/404",
    "https://httpstat.us/500"
  ],
  "maxPages": 50,
  "maxLinksToCheck": 500,
  "checkExternalLinks": true,
  "checkAssets": false,
  "onlyReportBroken": true,
  "slowThresholdMs": 5000,
  "requestTimeoutMs": 15000,
  "maxConcurrency": 20,
  "maxRedirects": 10,
  "userAgent": "Mozilla/5.0 (compatible; ApifyBrokenLinkChecker/1.0; +https://apify.com)",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `downloadCsv` (type: `string`):

No description

## `output` (type: `string`):

Machine-readable terminal outcome for agent orchestration.

## `runSummary` (type: `string`):

Machine-readable verification totals, charged events, and outcome.

## `htmlReport` (type: `string`):

No description

## `summaryJson` (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 = {
    "startUrl": "https://crawler-test.com/links/broken_links_internal",
    "urls": [
        "https://example.com/",
        "https://example.com/this-page-does-not-exist",
        "https://httpstat.us/404",
        "https://httpstat.us/500"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/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 = {
    "startUrl": "https://crawler-test.com/links/broken_links_internal",
    "urls": [
        "https://example.com/",
        "https://example.com/this-page-does-not-exist",
        "https://httpstat.us/404",
        "https://httpstat.us/500",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/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 '{
  "startUrl": "https://crawler-test.com/links/broken_links_internal",
  "urls": [
    "https://example.com/",
    "https://example.com/this-page-does-not-exist",
    "https://httpstat.us/404",
    "https://httpstat.us/500"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call khadinakbar/broken-link-checker --silent --output-dataset

```

## MCP server setup

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