Markdown Broken Links Checker avatar

Markdown Broken Links Checker

Pricing

from $0.70 / 1,000 link checkeds

Go to Apify Store
Markdown Broken Links Checker

Markdown Broken Links Checker

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.

Pricing

from $0.70 / 1,000 link checkeds

Rating

0.0

(0)

Developer

ParseBird

ParseBird

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

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.

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.

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.

This actor is a hosted, scriptable version of the workflow behind 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?

FieldDescription
sourceThe Markdown URL (or inline-markdown-text) this link came from
lineLine number in the source document
linkTextThe link's visible text
urlThe link target (resolved against the source URL for relative links)
statusalive, dead, or ignored
statusCodeHTTP status code, or null for anchor/scheme-only checks
errorMessageReason for a dead or ignored result
isImagetrue for ![alt](url) image links
checkedAtWhen 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

ParameterTypeRequiredDefaultDescription
markdownUrlsstring[]No*[]Raw Markdown file URLs to check
markdownTextstringNo*""Markdown text to check directly, instead of or alongside URLs
ignorePatternsstring[]No[]Regexes; matching links are skipped and marked ignored
additionalAliveStatusCodesstring[]No[]Extra HTTP codes to treat as alive (e.g. 403, 429 for bot-blocking sites)
checkAnchorsbooleanNotrueValidate #fragment links against the document's own headings
timeoutSecondsintegerNo10Per-link request timeout (1–60)
retriesintegerNo1Retry attempts before marking a link dead (0–5)
concurrencyintegerNo10Max links checked in parallel (1–30)
userAgentstringNoa descriptive bot UASent with every link check
failOnDeadLinksbooleanNofalseFail 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

{
"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 / dataset endpoint.

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.
  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 or the run-sync endpoint's HTTP response.
  5. Run it — in the Apify Console, via the API, on a schedule, 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.

This actor uses Pay-Per-Event (PPE) pricing — you're charged per link checked, not per compute unit.

EventPrice per eventPrice 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 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 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.

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):

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):

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'));

Part of the ParseBird infrastructure toolset:

Browse the full catalog on the ParseBird Apify Store page.