# Markdown Broken Links Checker (`parsebird/markdown-broken-links-checker`) Actor

Check every hyperlink and anchor in your Markdown files for broken links. Point it at raw Markdown URLs or paste text directly — use it as a CI/CD gate before you deploy or publish.

- **URL**: https://apify.com/parsebird/markdown-broken-links-checker.md
- **Developed by:** [ParseBird](https://apify.com/parsebird) (community)
- **Categories:** Developer tools, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 link checkeds

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/platform/actors/running/actors-in-store#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

### Markdown Broken Links Checker

Check every hyperlink, image, and anchor in your Markdown files for broken links — before you deploy or publish. The **Markdown Broken Links Checker** points at raw Markdown URLs or takes pasted Markdown text, checks each link's live HTTP status, validates internal anchors against the document's own headings, and can fail the run to gate a CI/CD pipeline on broken links.

<table><tr>
<td style="border-left:4px solid #1C1917;padding:12px 16px;font-weight:600">
Get a per-link alive/dead report plus a pass/fail summary in one run — turn on "Fail run on dead links" and wire it straight into a pre-deploy CI/CD check.
</td>
</tr></table>

##### Copy to your AI assistant

Copy this block into ChatGPT, Claude, Cursor, or any LLM to start using this actor.

```
Apify Actor: parsebird/markdown-broken-links-checker. Checks Markdown files for broken hyperlinks, images, and anchor links. Call via ApifyClient: client.actor("parsebird/markdown-broken-links-checker").call(run_input={"markdownUrls": ["https://raw.githubusercontent.com/user/repo/main/README.md"], "checkAnchors": true, "failOnDeadLinks": true}). Returns per link: source, line, linkText, url, status (alive/dead/ignored), statusCode, errorMessage, isImage, checkedAt. Also writes a pass/fail SUMMARY record to the key-value store. Full API spec: https://apify.com/parsebird/markdown-broken-links-checker/api. Get your token: https://console.apify.com/settings/integrations.
```

### What does Markdown Broken Links Checker do?

This actor is a hosted, scriptable version of the workflow behind [markdown-link-check](https://github.com/tcort/markdown-link-check), the open-source CLI tool for finding dead links in Markdown. Give it one or more raw Markdown URLs (like a GitHub `raw.githubusercontent.com` README link), pasted Markdown text, or both, and it:

- 🔗 Extracts every inline link, image, autolink, and reference-style link (`[text][ref]`) from the document.
- 🌐 Checks each absolute `http(s)` link's live status with configurable timeout, retries, and concurrency.
- ⚓ Validates same-document anchor links (`#installation`) against the headings actually present in the file.
- 🚦 Reports each link as **alive**, **dead**, or **ignored**, plus an overall pass/fail summary.
- 🛑 Optionally fails the Actor run itself when dead links are found — the key feature for using this as a **pre-deploy CI/CD gate**, not just a report.

### What data does it return?

| Field | Description |
|-------|-------------|
| `source` | The Markdown URL (or `inline-markdown-text`) this link came from |
| `line` | Line number in the source document |
| `linkText` | The link's visible text |
| `url` | The link target (resolved against the source URL for relative links) |
| `status` | `alive`, `dead`, or `ignored` |
| `statusCode` | HTTP status code, or `null` for anchor/scheme-only checks |
| `errorMessage` | Reason for a dead or ignored result |
| `isImage` | `true` for `![alt](url)` image links |
| `checkedAt` | When this link was checked |

A `SUMMARY` record (`totalLinks`, `aliveCount`, `deadCount`, `ignoredCount`, `passed`) is also written to the run's key-value store for quick programmatic pass/fail checks without paging through the dataset.

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `markdownUrls` | string\[] | No\* | `[]` | Raw Markdown file URLs to check |
| `markdownText` | string | No\* | `""` | Markdown text to check directly, instead of or alongside URLs |
| `ignorePatterns` | string\[] | No | `[]` | Regexes; matching links are skipped and marked `ignored` |
| `additionalAliveStatusCodes` | string\[] | No | `[]` | Extra HTTP codes to treat as alive (e.g. `403`, `429` for bot-blocking sites) |
| `checkAnchors` | boolean | No | `true` | Validate `#fragment` links against the document's own headings |
| `timeoutSeconds` | integer | No | `10` | Per-link request timeout (1–60) |
| `retries` | integer | No | `1` | Retry attempts before marking a link dead (0–5) |
| `concurrency` | integer | No | `10` | Max links checked in parallel (1–30) |
| `userAgent` | string | No | a descriptive bot UA | Sent with every link check |
| `failOnDeadLinks` | boolean | No | `false` | Fail the Actor run if any dead links are found — turn on for CI/CD gating |

\*At least one of `markdownUrls` or `markdownText` is required.

### Output example

```json
{
  "source": "https://raw.githubusercontent.com/user/repo/main/README.md",
  "line": 42,
  "linkText": "Documentation",
  "url": "https://example.com/docs",
  "status": "dead",
  "statusCode": 404,
  "errorMessage": "HTTP 404",
  "isImage": false,
  "checkedAt": "2026-08-09T02:42:54.000Z"
}
```

Download results as **JSON, CSV, Excel, HTML, or XML** directly from the Apify Console, or fetch them via the [Apify API](https://docs.apify.com/api/v2) / [dataset endpoint](https://apify.com/parsebird/markdown-broken-links-checker/api).

### Use cases

- **Pre-deploy CI/CD gate** — run before a docs site build or npm publish; fail the pipeline if any link in your README or docs is dead.
- **Documentation maintenance** — schedule a weekly run against your repo's README and docs folder to catch link rot before users report it.
- **Pull request checks** — point at the raw URL of a changed Markdown file in a PR branch to verify new links before merge.
- **Content QA** — check blog posts or knowledge-base articles written in Markdown before publishing.

### How to use the Markdown Broken Links Checker

1. **Provide your Markdown** — add one or more raw Markdown URLs (e.g. `https://raw.githubusercontent.com/<user>/<repo>/<branch>/README.md`), paste Markdown text, or both.
2. **Set filters** (optional) — `ignorePatterns` for known-noisy links (localhost, mailto:), `additionalAliveStatusCodes` for sites that block bots but are actually fine.
3. **Tune request settings** (optional) — `timeoutSeconds`, `retries`, and `concurrency` for slower or rate-limit-sensitive targets.
4. **Turn on `failOnDeadLinks`** if you want this run to act as a gate — the Actor run itself will fail when dead links are found, which your CI system can detect via the [Apify API run status](https://docs.apify.com/api/v2#/reference/actor-runs/run-object/get-run) or the [`run-sync` endpoint](https://docs.apify.com/api/v2#/reference/actors/run-actor-synchronously)'s HTTP response.
5. **Run it** — in the Apify Console, via the [API](https://docs.apify.com/api/v2), on a [schedule](https://docs.apify.com/platform/schedules), or as a step in your CI pipeline (GitHub Actions, GitLab CI, Jenkins — anything that can make an HTTP call).
6. **Read the results** — the per-link dataset for details, or the `SUMMARY` key-value store record for a quick pass/fail check.

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

This actor uses [Pay-Per-Event (PPE)](https://docs.apify.com/platform/actors/publishing/monetize#pay-per-event-pricing) pricing — you're charged per link checked, not per compute unit.

| Event | Price per event | Price per 1,000 |
|-------|----------------|-----------------|
| `link-checked` (Free plan) | $0.001 | **$1.00** |
| `link-checked` (Bronze plan) | $0.0009 | **$0.90** |
| `link-checked` (Silver plan) | $0.0008 | **$0.80** |
| `link-checked` (Gold plan) | $0.0007 | **$0.70** |

A run checking a typical 30-link README costs about $0.02–$0.03. All Apify accounts include a [free trial](https://apify.com/pricing) with credits to try the actor before committing to a paid plan.

### FAQ

**How do I use this as a CI/CD gate?**
Set `failOnDeadLinks: true`. When dead links are found, the Actor run itself fails — call it via the Apify API's [run-sync endpoint](https://docs.apify.com/api/v2#/reference/actors/run-actor-synchronously) from a GitHub Actions/GitLab CI/Jenkins step and check the HTTP response status or the run's `status` field to gate your build. The per-link dataset and the `SUMMARY` key-value record stay available even when the run fails, so you can see exactly which links broke.

**Why is a link I know is valid marked `dead`?**
Some sites (npm, LinkedIn, and others) return `403`, `405`, or `429` to automated requests even though the page works fine in a browser. Add the relevant code to `additionalAliveStatusCodes` to treat it as alive.

**Does it check relative links?**
Yes, when checking a Markdown URL — relative links are resolved against that URL. Relative links in pasted `markdownText` (no source URL to resolve against) are marked `ignored` since there's no base to resolve them from.

**How accurate is anchor checking?**
It generates GitHub-style heading slugs (lowercase, punctuation stripped, spaces → hyphens, numbered suffixes for duplicate headings) from ATX headings (`# Heading`) in the same document. It's a best-effort match and doesn't parse Setext-style headings (underlined with `===`/`---`).

**What counts as a link?**
Inline links `[text](url)`, images `![alt](url)`, reference-style links `[text][ref]`, and autolinks `<https://...>` and bare URLs. `mailto:`, `tel:`, `data:`, and `javascript:` links are marked `ignored` rather than checked over HTTP.

### Is it legal to use this actor?

Yes. This actor only sends standard HTTP HEAD/GET requests to URLs you explicitly provide, the same as any browser or uptime monitor would. It does not bypass authentication or access private content. Users are responsible for ensuring their use complies with the target sites' terms of service and applicable rate-limiting expectations — lower `concurrency` if a target site starts blocking your checks.

### Python and JavaScript usage

**Python** ([apify-client](https://docs.apify.com/api/client/python/)):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run_input = {
    "markdownUrls": ["https://raw.githubusercontent.com/user/repo/main/README.md"],
    "checkAnchors": True,
    "failOnDeadLinks": True,
}

run = client.actor("parsebird/markdown-broken-links-checker").call(run_input=run_input)

summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("SUMMARY")
print(summary["value"])

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["status"] == "dead":
        print(item["url"], item["errorMessage"])
```

**JavaScript** ([apify-client](https://docs.apify.com/api/client/js/)):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });

const input = {
    markdownUrls: ['https://raw.githubusercontent.com/user/repo/main/README.md'],
    checkAnchors: true,
    failOnDeadLinks: true,
};

const run = await client.actor('parsebird/markdown-broken-links-checker').call(input);

const { value: summary } = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('SUMMARY');
console.log(summary);

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.status === 'dead'));
```

### Related Actors

Part of the ParseBird infrastructure toolset:

- [HTTP Request Actor](https://apify.com/parsebird/http-request-actor) — send arbitrary HTTP requests from a pipeline
- [Data Deduplicator](https://apify.com/parsebird/dataset-deduplicator) — dedupe records in any Apify dataset
- [Data Cleaner](https://apify.com/parsebird/data-cleaner) — clean and normalize dataset fields

Browse the full catalog on the [ParseBird Apify Store page](https://apify.com/parsebird).

# Actor input Schema

## `markdownUrls` (type: `array`):

Raw Markdown file URLs to check (e.g. a GitHub raw README URL). Each file is checked separately and reported under its own URL.

## `markdownText` (type: `string`):

Paste Markdown content directly to check, instead of or in addition to Markdown URLs.

## `ignorePatterns` (type: `array`):

Regular expressions. Links matching any pattern are skipped and marked "ignored" (e.g. "^http://localhost", "^mailto:").

## `additionalAliveStatusCodes` (type: `array`):

Treat these HTTP status codes as alive in addition to 2xx. Useful for sites that block automated checks (e.g. 403 or 429) but are otherwise reachable.

## `checkAnchors` (type: `boolean`):

Validate same-document links like "#installation" against the file's own headings.

## `timeoutSeconds` (type: `integer`):

Per-link request timeout.

## `retries` (type: `integer`):

Retry attempts for a link before marking it dead.

## `concurrency` (type: `integer`):

Maximum links checked in parallel. Lower this if a target site starts rate-limiting the check.

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

Sent with every link check. Some sites block default bot user agents.

## `failOnDeadLinks` (type: `boolean`):

When on, the Actor run fails (non-zero status) if any dead links are found — turn this on to gate a CI/CD pipeline on broken links.

## Actor input object example

```json
{
  "markdownUrls": [
    "https://raw.githubusercontent.com/tcort/markdown-link-check/master/README.md"
  ],
  "markdownText": "",
  "ignorePatterns": [],
  "additionalAliveStatusCodes": [],
  "checkAnchors": true,
  "timeoutSeconds": 10,
  "retries": 1,
  "concurrency": 10,
  "userAgent": "Mozilla/5.0 (compatible; MarkdownBrokenLinksChecker/1.0; +https://apify.com/parsebird/markdown-broken-links-checker)",
  "failOnDeadLinks": false
}
```

# Actor output Schema

## `dataset` (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 = {
    "markdownUrls": [
        "https://raw.githubusercontent.com/tcort/markdown-link-check/master/README.md"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("parsebird/markdown-broken-links-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 = { "markdownUrls": ["https://raw.githubusercontent.com/tcort/markdown-link-check/master/README.md"] }

# Run the Actor and wait for it to finish
run = client.actor("parsebird/markdown-broken-links-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 '{
  "markdownUrls": [
    "https://raw.githubusercontent.com/tcort/markdown-link-check/master/README.md"
  ]
}' |
apify call parsebird/markdown-broken-links-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parsebird/markdown-broken-links-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/LFxMesP5r2c342ghh/builds/hrDP8cyMs7MjmW8Oc/openapi.json
