Link Status Checker
Pricing
from $1.79 / 1,000 results
Link Status Checker
Check internal and external links discovered while crawling user-supplied start URLs. Each result includes the target, status category, HTTP code, final URL, redirect chain, anchor text and source page. The checker uses HEAD requests with a GET fallback and records redirect hops for review.
Pricing
from $1.79 / 1,000 results
Rating
0.0
(0)
Developer
Axiom Works
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 hours ago
Last modified
Categories
Share
Returns: one row per link per crawled page with url, statusCode, linkStatus, isBroken, anchorText, sourcePage, redirectChain and finalUrl.
Input: {"startUrls": [{"url": "https://crawler-test.com/links/broken_links_internal"}], "maxDepth": 1, "maxPages": 20, "maxItems": 500}
Best for: finding 404s before an SEO audit, checking outbound links after a site migration, QA of redirect chains.
{"id": "d6af6e0cd46f477b","url": "https://crawler-test.com/","anchorText": "Crawler Test two point oh!","sourcePage": "https://crawler-test.com/links/broken_links_internal","linkType": "internal","statusCode": 200,"isBroken": false,"linkStatus": "ok","redirectChain": [],"finalUrl": "https://crawler-test.com/","method": "HEAD"}
What does Broken Link Checker do?
Broken Link Checker crawls a website from one or more start URLs and checks every link it finds. It stays on the start site (subdomains included by default), follows internal pages up to the depth and page limits you set, and checks each internal and outbound link with an HTTP HEAD request, falling back to GET when a server doesn't answer HEAD properly. Redirects are followed one hop at a time, so you see the whole redirect chain and where the link ends up.
You get one row for every link on every crawled page: the link URL as written and as resolved, the anchor text, the page it was found on, the final HTTP status code, the redirect chain, the final URL, the content type, the response time and a plain category such as ok, redirect, broken, blocked, server_error, network_error or redirect_loop. A link that appears on ten pages gives ten rows (one per page, so you know where to fix it), but it is checked only once per run.
It works on any public website and needs no login, no API key and no browser. Typical jobs are a broken link report for an SEO audit, checking a site after a migration or redesign, monitoring outbound links in a blog or documentation site, and finding long redirect chains that slow pages down. It is a website link checker and dead link finder that you can run on a schedule or call from an AI agent.
What data can you get from a website link check?
| Field | Description | Example |
|---|---|---|
id | Stable record ID: first 16 hex characters of sha1(sourcePage + "|" + url) | b2377af70dd16694 |
url | The link target that was checked: the href resolved to an absolute URL, #fragment removed | https://crawler-test.com/links/not_found/foo1 |
href | The href attribute exactly as written in the page | /links/not_found/foo1 |
anchorText | Visible text of the link, whitespace collapsed; null for image-only links | Broken Internal Link 1 |
rel | The link's rel attribute, null when absent | nofollow |
occurrences | How many times the page links to this URL | 1 |
sourcePage | Final URL of the crawled page the link was found on | https://crawler-test.com/links/broken_links_internal |
sourceDepth | Crawl depth of the source page (0 = a start URL) | 0 |
linkType | internal (same site or subdomain) or external | internal |
statusCode | Final HTTP status code; null on a network error or redirect loop | 404 |
isBroken | true when statusCode is null or 400 or higher | true |
linkStatus | ok, redirect, broken, blocked, server_error, network_error or redirect_loop | broken |
redirectCount | Number of redirect hops followed | 4 |
redirectChain | Every redirect hop in order as {url, statusCode} | [{"url": "https://crawler-test.com/redirects/reverse_redirect/4", "statusCode": 301}] |
finalUrl | Where the link ends up after redirects | https://crawler-test.com/redirects/redirect_target |
contentType | MIME type of the final response | text/html |
method | HEAD, or GET when the server needed the GET fallback | HEAD |
error | Network error code when no response came back (ENOTFOUND, TOO_MANY_REDIRECTS...) | ENOTFOUND |
responseTimeMs | Milliseconds the check took, summed over redirect hops | 317 |
checkedAt | ISO 8601 time the check finished | 2026-10-13T01:04:53.940Z |
sourceUrl | The page the link was found on (same as sourcePage) | https://crawler-test.com/links/broken_links_internal |
Requests to each host start at least 250 milliseconds apart, with at most two requests in flight per host and the overall maxConcurrency limit. Results remain in page order, and repeated target URLs share one cached check across pages.
At the end of every run a SUMMARY record in the run's key-value store lists the start URLs, the number of pages crawled, links found, unique URLs checked, broken links, redirects, blocked links and network errors, the pages that could not be crawled (pageErrors) and why the crawl stopped (maxItems, maxPages or exhausted).
What the link statuses mean
| linkStatus | When | isBroken |
|---|---|---|
ok | Status below 400 and no redirect | false |
redirect | The link redirected at least once and ended below 400 | false |
broken | Final status 400-499 (except 403 and 429) | true |
blocked | Final status 403, 429 or 999: usually bot protection rather than a dead page | true |
server_error | Final status 500 or higher | true |
network_error | No response: DNS failure, refused connection, timeout, bad certificate | true |
redirect_loop | More redirect hops than maxRedirects | true |
How to use Broken Link Checker
- Open Broken Link Checker in Apify Console and go to the Input tab.
- Add one or more start URLs. A full URL (
https://www.example.com/blog/) or just a host (example.com) both work. - Set how far to crawl:
maxDepth(clicks from the start page),maxPages(pages scanned for links) andmaxItems(rows returned). - Optionally switch off
checkExternalLinksto check only your own site, or switch ononlyBrokento get only the problems. - Click Start. When the run finishes, open the Output tab and filter by
linkStatusorisBroken, or export the rows as JSON, CSV, Excel, XML or HTML.
To audit exactly the pages you publish, give your sitemap, for example https://example.com/sitemap.xml, as a start URL. Its page URLs are checked directly, so maxDepth 0 checks just the links on those pages. Gzipped sitemaps (.xml.gz) are not read.
Start with the defaults (maxDepth 1, maxPages 20). They check the start page and the pages it links to, which covers the navigation of most small sites in a minute or two.
For a full site audit, raise maxPages and maxItems together. maxItems is shared across all start URLs and pages; the run stops as soon as that many rows are saved.
Use excludeUrlPatterns to skip logout links, admin areas, calendars or faceted search pages that would otherwise produce thousands of near-identical URLs.
Schedule the Actor weekly in the Schedules tab to catch new broken links early.
Input
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | Yes | Websites or pages to start from, as [{"url": "..."}] or plain strings. A bare host gets https://. A URL ending in .xml is read as a sitemap (or sitemap index, first 5 child sitemaps): its page URLs, up to maxPages, are checked as start pages. The legacy keys url / startUrl (one string) and urls (list) are accepted too. |
maxDepth | integer | No | Clicks deep to crawl, 0-10, default 1. 0 checks only the links on the start pages. |
maxPages | integer | No | Maximum HTML pages crawled across all start URLs, 1-10000, default 20. |
maxItems | integer | No | Maximum link rows returned, 1-1000000, default 500. |
checkExternalLinks | boolean | No | Check and return links to other websites, default true. They are never crawled. |
includeSubdomains | boolean | No | Treat subdomains as the same site, default true. |
onlyBroken | boolean | No | Return only rows with isBroken true, default false. Everything is still crawled and checked. |
excludeUrlPatterns | array of strings | No | Regular expressions; matching URLs are not crawled, checked or returned. Default: none. |
maxRedirects | integer | No | Redirect hops followed per link, 0-30, default 10. |
requestTimeoutSecs | integer | No | Seconds to wait for a response, 3-120, default 15. |
maxConcurrency | integer | No | Checks in flight at once, 1-20, default 5 (never more than 2 per host). |
userAgent | string | No | User-Agent header. Default: a current desktop Chrome User-Agent. |
proxyConfiguration | object | No | Proxy for every request. Default: no proxy. |
{"startUrls": [{ "url": "https://crawler-test.com/links/broken_links_internal" },"iana.org"],"maxDepth": 1,"maxPages": 10,"maxItems": 200,"checkExternalLinks": true,"includeSubdomains": true,"onlyBroken": false,"excludeUrlPatterns": ["logout", "/wp-admin/"],"maxRedirects": 10,"requestTimeoutSecs": 15,"maxConcurrency": 5,"proxyConfiguration": { "useApifyProxy": false }}
Output
Each row is one link on one page. These rows come from a real run on crawler-test.com and iana.org. You can download the dataset as JSON, CSV, Excel, XML, RSS or HTML, or read it through the API.
[{"id": "6a4540477f1fd18d","url": "https://crawler-test.com/redirects/reverse_redirect/4","href": "/redirects/reverse_redirect/4","anchorText": "4 Redirects Chain Link","rel": null,"occurrences": 1,"sourcePage": "https://crawler-test.com/redirects/url_redirect_chains","sourceDepth": 0,"linkType": "internal","statusCode": 200,"isBroken": false,"linkStatus": "redirect","redirectCount": 4,"redirectChain": [{ "url": "https://crawler-test.com/redirects/reverse_redirect/4", "statusCode": 301 },{ "url": "https://crawler-test.com/redirects/reverse_redirect/3", "statusCode": 301 },{ "url": "https://crawler-test.com/redirects/reverse_redirect/2", "statusCode": 301 },{ "url": "https://crawler-test.com/redirects/reverse_redirect/1", "statusCode": 301 }],"finalUrl": "https://crawler-test.com/redirects/redirect_target","contentType": "text/html","method": "HEAD","error": null,"responseTimeMs": 1365,"checkedAt": "2026-10-13T01:20:16.614Z","sourceUrl": "https://crawler-test.com/redirects/url_redirect_chains"},{"id": "09a5c607ebe781fb","url": "https://www.icann.org/","href": "https://www.icann.org/","anchorText": "ICANN","rel": null,"occurrences": 1,"sourcePage": "https://www.iana.org/","sourceDepth": 0,"linkType": "external","statusCode": 403,"isBroken": true,"linkStatus": "blocked","redirectCount": 0,"redirectChain": [],"finalUrl": "https://www.icann.org/","contentType": "text/html","method": "GET","error": null,"responseTimeMs": 68,"checkedAt": "2026-10-13T01:19:28.030Z","sourceUrl": "https://www.iana.org/"}]
The Output tab's Overview shows the most useful columns (status, link, anchor text, source page, link type, redirects, final URL, error); the All fields view shows everything.
How much does it cost to check links on a website?
Broken Link Checker is charged per result, where one result is one link row. You pay only for the rows returned, and maxItems caps how many that can be. With onlyBroken on you pay only for broken rows, even though every link is checked. See the Pricing tab for the current price.
Use with AI agents (MCP)
Connect Broken Link Checker to Claude, Cursor or any MCP client through the Apify MCP server:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com/?tools=axiomworks/broken-link-checker"}}}
Example prompts:
- "Check https://www.example.com for broken links two clicks deep and list every 404 with the page it's on."
- "Find all outbound links on our blog at blog.example.com that redirect more than twice, with their final URLs."
- "Run a broken link check on example.org with onlyBroken on and group the results by linkStatus."
Integrations and API
Run Broken Link Checker from your own code with the Apify API. Set APIFY_TOKEN to your Apify API token (send it in the Authorization header, never in the URL).
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run = client.actor("axiomworks/broken-link-checker").call(run_input={"startUrls": [{"url": "https://crawler-test.com/links/broken_links_internal"}],"maxDepth": 1,"maxItems": 500,"onlyBroken": True,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["linkStatus"], item["statusCode"], item["url"], "on", item["sourcePage"])
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });const run = await client.actor('axiomworks/broken-link-checker').call({startUrls: [{ url: 'https://crawler-test.com/links/broken_links_internal' }],maxDepth: 1,maxItems: 500,});const { items } = await client.dataset(run.defaultDatasetId).listItems();const broken = items.filter((item) => item.isBroken);
cURL
curl -X POST "https://api.apify.com/v2/acts/axiomworks~broken-link-checker/run-sync-get-dataset-items" \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"startUrls": [{"url": "https://crawler-test.com/links/broken_links_internal"}], "maxDepth": 1, "maxItems": 100}'
The Integrations tab connects runs to Make, Zapier, n8n, Google Sheets, Slack, email and webhooks, so a weekly scheduled check can send its broken links straight to your team.
FAQ
How is a link judged broken?
A link is broken (isBroken true) when its final HTTP status is 400 or higher, when no response came back at all (DNS failure, refused connection, timeout, certificate error) or when it redirects more often than maxRedirects. The linkStatus field tells these cases apart.
Why are some working links reported as blocked?
Many large sites (social networks, sites behind Cloudflare and similar services) answer automated requests with 403, 429 or LinkedIn's 999 even when the page works in a browser. These rows get linkStatus blocked so you can filter them out or check them by hand. They still count as isBroken true because the checker could not confirm the page works.
Why does a Facebook link show as broken? Facebook often redirects anonymous visitors to a login page that answers 400. The row shows the redirect chain and the final URL, so you can see what happened. Treat social profile links with care.
Why did a page that says "not found" come back as ok?
Some sites show a "page not found" message with HTTP status 200 (a soft 404). The checker reports the HTTP status the server sends, so such pages count as working.
Why did my run return 0 results?
If the start URL itself can't be loaded (wrong address, the site is down, or it blocks the request) the run ends with the status message "Start URL unreachable" and the reason, and the SUMMARY record lists it under pageErrors. If the page loads but has no links, the run finishes with 0 results and says so. With onlyBroken on, 0 results means no broken links were found.
Does it crawl other websites?
No. Only pages on the start URL's site (and its subdomains, unless you turn includeSubdomains off) are crawled. Links to other sites are checked once and returned, never crawled.
Does it read robots.txt or run JavaScript? It does not read robots.txt, and it reads the HTML the server sends without running JavaScript, so links that a page adds with JavaScript after loading are not found.
How fast is it? It is polite by default: at most 2 requests at a time to one host with a short pause between them, and 5 checks in flight overall. A start page plus a few hundred links usually takes one to two minutes.
Is it legal to crawl a website and check its links?
Broken Link Checker requests only public pages, the same way a browser or a search engine does, and does not log in anywhere. Checking links on your own site, or on public pages, is normal practice for site maintenance and SEO. Check the terms of sites you don't own before crawling them in depth, and keep the default polite request rate. This is not legal advice.
Broken Link Checker is an independent tool. It is not affiliated with, endorsed by or sponsored by any of the websites it checks.
Related Actors
| Actor | What it does |
|---|---|
| Semrush SEO Metrics Scraper | Domain-level SEO metrics to go with a link audit |
| Google Search SERP Scraper | Google search results for checking which of your pages rank |
Feedback
Found a bug or need a field that isn't here? Open an issue on the Issues tab and we'll look at it.