Broken Link Checker & Redirect Chain Auditor
Pricing
from $0.50 / 1,000 link checkeds
Broken Link Checker & Redirect Chain Auditor
Crawl a website to find broken links (404, 5xx), redirect chains, redirect loops, timeouts and mixed content. Polite, robots.txt-aware link audit.
Pricing
from $0.50 / 1,000 link checkeds
Rating
0.0
(0)
Developer
Grace M. Hartwell
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
20 hours ago
Last modified
Categories
Share
Broken Link Checker & Redirect Chain Auditor crawls your website and finds broken links (404, 410, other 4xx, 5xx), redirect chains, redirect loops, timeouts and insecure http / mixed-content links. It checks every internal and external link once (HEAD first, GET as a fallback) and records every redirect hop. You get one clear report row per link, showing the pages where the link appears, so you know exactly what to fix. The crawler respects robots.txt and per-host limits, and it costs $0.50 per 1,000 links checked.
What does the broken link checker do?
- Crawls the internal HTML pages of your site from a start URL, within a maximum depth and page count. robots.txt is respected and crawling is rate-limited.
- Collects every link (
<a>,<area>) and, optionally, every image, script, stylesheet, iframe and audio/video source, together with the pages and anchor texts where it appears. - Checks each unique URL once:
HEADfirst, thenGETwhen a server answers HEAD with an error code. Redirects are followed manually, so every hop is recorded. - Retries 429, 5xx and network errors with exponential backoff. Timeouts are not retried, which keeps runs fast.
- Reports one dataset item per link (internal links first, then external) and saves a run summary to the
OUTPUTrecord.
What does it find?
| Finding | status / issue code |
|---|---|
| 404, 410 and other 4xx, 5xx responses | broken / BROKEN (with the final HTTP status) |
| Redirects, and redirect chains of 2+ hops (every hop listed) | redirect / REDIRECT, REDIRECT_CHAIN |
| Redirect loops (A → B → A) | redirect-loop / REDIRECT_LOOP |
More redirects than maxRedirects | too-many-redirects / TOO_MANY_REDIRECTS |
| No answer within the timeout | timeout / TIMEOUT |
DNS, TLS and connection errors, invalid Location headers | error / ERROR (e.g. ENOTFOUND in error) |
https page linking to an http:// URL | INSECURE_HTTP_LINK |
https page loading an http:// image, script, CSS, iframe or media file | MIXED_CONTENT (needs checkResources) |
| Redirect from https to http | HTTPS_TO_HTTP_REDIRECT |
| http link that redirects to https | HTTP_UPGRADED_BY_REDIRECT (update the link) |
| Internal URL disallowed by robots.txt | skipped-robots / ROBOTS_TXT_DISALLOWED (not requested, not charged) |
Key features
- 🔗 Internal and external link checking: external sites get one HEAD/GET check per unique URL and are never crawled.
- ↪️ Full redirect chains: each hop with its URL, status code and
Location, plus the final URL and status. - 📍 Where each link appears: up to 10 pages with anchor text and tag (
foundOn), plus the total number of occurrences (foundOnCount). - 🔒 HTTPS hygiene: finds insecure
http://links, mixed content, and https → http redirects. - 🤝 Polite crawling: robots.txt is respected for your site, at most 2 parallel requests per host by default, rate limits for crawling, and a clear user agent (
BrokenLinkRedirectAuditor/1.0). - 🧾 Issues-only mode:
onlyIssueskeeps healthy links out of the dataset (they are still checked and counted). - 💸 Budget control:
maxLinksToCheckand Max total charge stop the run cleanly.
Use cases
- Technical SEO audits: find 404s and long redirect chains that waste crawl budget and link equity.
- Website migrations: check that old URLs redirect in one hop to the right target, with no loops or https → http downgrades.
- Content QA before a launch: catch dead internal links, missing images and broken scripts before visitors do.
- Scheduled link monitoring: run weekly on a blog, help center or documentation site with an Apify schedule.
- HTTPS clean-up: list remaining
http://links and mixed-content resources on https pages. - Outbound link hygiene: find external links that now return 404 or redirect somewhere else.
How much does it cost to check broken links?
The Actor uses pay-per-event pricing:
| Event | Price | When it is charged |
|---|---|---|
link-checked | $0.0005 per unique URL checked ($0.50 per 1,000) | After a link has been checked (crawling pages is included) |
apify-actor-start | $0.00005 per run | Automatically at run start (Apify default price) |
Each URL is checked and charged once per run, no matter how many pages link to it. URLs skipped because of robots.txt are free. Healthy links hidden by onlyIssues are still checked, so they are still charged.
Example calculation: a 200-page site with 1,500 unique links:
1,500 unique links × $0.0005 = $0.75+ 1 Actor start × $0.00005 = $0.00005 (default 512 MB memory → 1 start event)-------------------------------------------------Total ≈ $0.75005 (= $0.50 per 1,000 links)
How to find broken links on your website
- Open Broken Link Checker & Redirect Chain Auditor in Apify Store and click Try for free.
- Enter your Website (start URL), e.g.
https://www.example.org/. - Set Max pages to crawl and Max crawl depth (0 = only the start page).
- Choose what to check: external links, images, scripts, stylesheets and iframes (needed for mixed content), and whether to only output links with issues.
- Optional: set Max links to check or a Max total charge to limit the cost.
- Click Start. Open the Overview table, or export the report as CSV, Excel or JSON. The summary is in the
OUTPUTrecord.
Input example
{"startUrl": "https://www.example.org/","maxPages": 200,"maxDepth": 3,"includeSubdomains": false,"checkExternalLinks": true,"checkResources": true,"onlyIssues": true,"maxLinksToCheck": 0,"requestTimeoutSecs": 15,"maxRedirects": 10,"maxRetries": 2,"maxConcurrency": 5,"perHostConcurrency": 2,"maxRequestsPerMinute": 120}
| Input field | Type | Default | Description |
|---|---|---|---|
startUrl | string | https://example.com/ | Page to start from. Only pages on this host are crawled. Replace the default with your own website. |
maxPages | integer | 50 | Internal HTML pages to crawl (1–10,000). |
maxDepth | integer | 3 | 0 = only the start page, 1 = plus the pages it links to, … (0–20). |
includeSubdomains | boolean | false | Treat blog.example.com as internal when starting at example.com. |
checkExternalLinks | boolean | true | Also check links to other websites (never crawled). |
checkResources | boolean | false | Also check images, scripts, stylesheets, iframes and media; detects mixed content. |
onlyIssues | boolean | false | Leave healthy links out of the dataset. |
maxLinksToCheck | integer | 0 | 0 = no limit; also capped by Max total charge. |
requestTimeoutSecs | integer | 15 | Timeout per request (3–120). |
maxRedirects | integer | 10 | Longer chains become too-many-redirects (1–30). |
maxRetries | integer | 2 | Retries for 429, 5xx and network errors (0–6). |
maxConcurrency | integer | 5 | Parallel crawl requests (1–10); link checks use up to 2× this. |
perHostConcurrency | integer | 2 | Max parallel link checks per host (1–5). |
maxRequestsPerMinute | integer | 120 | Rate limit for crawling your site (1–1,000). |
Output example
One dataset item per unique link (example based on a local test run; the host has been replaced):
{"url": "https://www.example.org/chain-1","status": "redirect","httpStatus": 301,"finalUrl": "https://www.example.org/ok","finalStatus": 200,"redirectCount": 3,"redirectChain": [{ "url": "https://www.example.org/chain-1", "status": 301, "location": "https://www.example.org/chain-2" },{ "url": "https://www.example.org/chain-2", "status": 302, "location": "https://www.example.org/chain-3" },{ "url": "https://www.example.org/chain-3", "status": 307, "location": "https://www.example.org/ok" }],"internal": true,"kind": "anchor","issues": ["REDIRECT", "REDIRECT_CHAIN"],"mixedContentOn": [],"insecureLinkOn": [],"foundOn": [{ "page": "https://www.example.org/", "text": "Redirect chain", "tag": "a" }],"foundOnCount": 1,"method": "HEAD","responseTimeMs": 12,"error": null,"checkedAt": "2026-10-25T22:59:05.500Z"}
Robots.txt-skipped item (free):
{ "url": "https://www.example.org/private/secret", "status": "skipped-robots", "internal": true, "issues": ["ROBOTS_TXT_DISALLOWED"], "foundOn": [{ "page": "https://www.example.org/", "text": "Private", "tag": "a" }], "foundOnCount": 1, "note": "Not requested: disallowed by robots.txt. Not charged." }
Run summary (OUTPUT record, numbers from the local test site):
{"startUrl": "https://www.example.org/","pagesCrawled": 8,"uniqueLinksFound": 17,"linksChecked": 16,"robotsSkipped": 1,"byStatus": { "skipped-robots": 1, "ok": 8, "broken": 4, "redirect": 1, "redirect-loop": 1, "timeout": 2 },"byIssue": { "BROKEN": 4, "REDIRECT": 1, "REDIRECT_CHAIN": 2, "REDIRECT_LOOP": 1, "TIMEOUT": 2 },"stoppedEarly": false}
Output fields
| Field | Description |
|---|---|
url | The checked URL (fragment removed). |
status | ok, redirect, broken, timeout, error, redirect-loop, too-many-redirects, skipped-robots. |
httpStatus | Status of the first response (e.g. 301), or null. |
finalUrl, finalStatus | Where the redirects end and that page's status. |
redirectCount, redirectChain | Number of hops and { url, status, location } per hop. |
internal | true if the URL is on your site (subdomains only with includeSubdomains). |
kind | anchor (link) or resource (image, script, CSS, iframe, media). |
issues | Issue codes, e.g. BROKEN, REDIRECT_CHAIN, MIXED_CONTENT. |
mixedContentOn, insecureLinkOn | Up to 10 https pages that load the resource over http / link to it over http. |
foundOn, foundOnCount | Up to 10 pages with anchor text and tag; total number of occurrences. |
method | HEAD or GET (the method used for the last request). |
responseTimeMs | Total time for the check, including redirects and retries. |
error | Readable error, e.g. HTTP 404 or HTTP 403 (access denied – may be login/bot protection). |
checkedAt | Timestamp. |
Integrations and API
The report is a standard Apify dataset, so you can use the Apify API, API clients, schedules, webhooks and integrations (for example Make, Zapier, n8n or Google Sheets exports).
curl -X POST "https://api.apify.com/v2/acts/grace_hartwell~broken-link-redirect-auditor/run-sync-get-dataset-items?token=[YOUR_APIFY_TOKEN]" \-H "Content-Type: application/json" \-d '{"startUrl":"https://www.example.org/","maxPages":50,"onlyIssues":true}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('grace_hartwell/broken-link-redirect-auditor').call({ startUrl: 'https://www.example.org/', onlyIssues: true });const { items } = await client.dataset(run.defaultDatasetId).listItems();const broken = items.filter((i) => i.status === 'broken');
FAQ
Why is a link that works in my browser reported as 403?
Some sites block non-browser clients (bot protection) or require a login. The item then says HTTP 403 (access denied – may be login/bot protection). Check these links manually.
Does the link checker render JavaScript?
No. Pages are fetched as HTML without a browser, so links that are injected only by client-side JavaScript are not found.
How is a redirect chain counted?
redirectCount is the number of redirect hops. One hop is reported as REDIRECT; two or more hops also raise REDIRECT_CHAIN.
Are external websites crawled?
No. External links get a single check (HEAD, with a GET fallback when needed) per unique URL. Only pages on your start host (and, optionally, its subdomains) are crawled.
Does it check image, script and CSS links?
Yes, if you turn on checkResources. This is also needed to detect mixed content.
How do I keep the cost under control?
Use maxPages, maxDepth, maxLinksToCheck or a Max total charge. The Actor checks internal links first and stops cleanly at the limit; stoppedEarly in the summary tells you whether the limit was hit.
Is www.example.com treated as the same site as example.com?
Yes. A leading www. is ignored when deciding whether a URL is internal.
Limitations: what this Actor does not do
- No JavaScript rendering: links added only by client-side scripts are missed.
- No logins, form submissions or bot-protection bypass. Protected pages may show up as 401/403
broken. - Fragments are not checked:
#anchorparts are removed, so missing in-page anchors are not detected. - Only http(s) links:
mailto:,tel:,javascript:,data:,ftp:and similar schemes are ignored. - No sitemap import: pages are discovered only by following links from the start URL, within
maxDepthandmaxPages. - Files are checked but not crawled: URLs ending in
.pdf,.zip, images,.css,.jsand similar are checked, but not parsed for more links. - robots.txt applies to your site only: external links are checked without reading their robots.txt (one request per unique URL). robots.txt rules are read from the start host only.
- Timeouts can take up to about twice
requestTimeoutSecs: when the HEAD request times out, a GET request is tried as well, and timeouts are not retried. - Link checks are not limited per minute:
maxRequestsPerMinuteapplies to crawling, while link checks are limited byperHostConcurrencyand the number of workers. - Crawled pages are requested twice (once to crawl and once to check), but they are charged once.
- No content extraction: apart from links, anchor text and tags, nothing is extracted from your pages.
Author: Grace M. Hartwell