Site Health Scanner avatar

Site Health Scanner

Pricing

Pay per usage

Go to Apify Store
Site Health Scanner

Site Health Scanner

Crawl a website to detect broken and problematic links, identify redirects and blocked URLs, capture screenshots, and return structured site health data for audits, automation, and monitoring.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

CQ

CQ

Maintained by Community

Actor stats

0

Bookmarked

28

Total users

2

Monthly active users

a day ago

Last modified

Share

Site Health Scanner ๐Ÿ”

Find broken links before your users do. The scanner crawls your site, checks every link, and captures screenshots of broken internal pages as client-ready proof.

โš ๏ธ Bot Protection Notice: Some websites use aggressive bot detection that blocks automated requests. You can enable the Residential Proxy setting to bypass basic protection, but heavily protected sites (Cloudflare, advanced WAFs) may still block scans. Links on those sites are marked as BLOCKED rather than broken โ€” verify them manually in a browser.

What does it do?

Site Health Scanner uses a real (Playwright) browser to crawl your website and check every link it finds โ€” internal pages, external URLs, images, scripts, and stylesheets. When it finds a broken internal page, it can take a screenshot so you have visual proof for clients or stakeholders.

Perfect for:

  • SEO professionals auditing client sites
  • Web agencies delivering health reports
  • Site owners maintaining link integrity
  • QA teams catching issues before launch

Features

FeatureDescription
๐Ÿ”— Broken Link DetectionFlags 4xx and 5xx responses, timeouts, and connection errors across your site
๐Ÿšซ Smart Bot-Block DetectionHeuristically separates truly broken links from bot-blocked sites (external, and widespread internal 403 patterns)
๐Ÿ“ธ Screenshot ProofCaptures screenshots of broken internal pages (see limitations below)
โ†ช๏ธ Redirect Chain TrackingFollows and records redirect chains (up to 10 hops), flags long chains
โš ๏ธ Mixed Content WarningsFlags HTTP resources linked from HTTPS start URLs
โฑ๏ธ Slow Response FlaggingRecords response time per link and warns when it exceeds 3 seconds
๐ŸŒ External Link CheckingOptionally verifies the status code of outbound links (not crawled)
๐Ÿ” Regression MonitoringOptional mode that compares each run to the prior run and can POST a webhook alert when new broken links appear

Pricing

This actor runs on the Apify platform, so you pay for the platform compute it consumes plus any optional residential-proxy traffic you enable. Cost scales with:

  • Number of pages crawled (maxPages) and crawl depth (maxDepth)
  • Whether external link checking is enabled (adds many more requests)
  • Whether screenshots are enabled (adds browser navigation + storage)
  • Whether the residential proxy is enabled (you pay for proxy traffic)

For current rates, see the pricing shown on this actor's Apify Store page. Use maxPages to cap costs on large sites.

Input

FieldTypeDescriptionDefault
startUrlsarrayURLs to start crawling (homepage, sitemap, or specific pages)Required
maxDepthintegerHow many clicks deep to crawl (0โ€“10). 0 = only start URLs3
maxPagesintegerMaximum pages to crawl (1โ€“10,000)100
checkExternalLinksbooleanAlso check the status of links to other domainstrue
screenshotBrokenPagesbooleanScreenshot broken internal pagestrue
followRedirectsbooleanFollow and record redirect chainstrue
timeoutintegerPer-request timeout in seconds (5โ€“120)30
includeWarningsbooleanPopulate the warning field (mixed content, slow, long redirects, bot-protection notices)true
userAgentstringCustom user-agent string. Leave empty to rotate realistic browser UAs""
useProxybooleanRoute requests through Apify residential proxy (adds proxy traffic cost)false
requestDelayintegerDelay between requests in ms (0โ€“10000) to avoid rate limiting. External links use 3ร— this value0
monitoringModebooleanPersist broken-link state per start URL and report new vs. fixed links against the prior runfalse
alertWebhookUrlstringOptional URL to POST a JSON alert to when new broken links are detected (Slack, Discord, Zapier, custom)""

About Bot Protection (403 Errors)

Many websites block automated requests and return 403 Forbidden. That does not necessarily mean the link is broken โ€” the site may just be blocking bots.

Without proxy (default): External links may show a 403 BLOCKED status. These are recorded with isBroken: false and confidence: low because the link likely works for real users. If more than half of a sample of internal links return 403 (5+ samples), the scanner treats it as site-wide bot protection and also marks those as BLOCKED / isBroken: false.

With residential proxy: Enable useProxy to route requests through residential IPs, which are less likely to be blocked. This adds residential-proxy traffic cost.

About Rate Limiting (429 Errors)

If a site rate-limits requests (429 responses), increase requestDelay:

  • 0 (default): No delay โ€” fastest, but may trigger rate limits
  • 500โ€“1000: Light throttling โ€” good for most sites
  • 1500โ€“2000: Heavy throttling โ€” for aggressive sites
  • External links: Automatically use 3ร— the configured delay

Regression Monitoring

Set monitoringMode: true to store the set of broken URLs per start URL in the actor's key-value store. On the next run the scanner reports how many links are newly broken and how many are now fixed versus the prior run. If alertWebhookUrl is set, a JSON payload is POSTed only when new broken links appear โ€” ideal for scheduled runs.

Example Input

{
"startUrls": [{ "url": "https://example.com" }],
"maxDepth": 3,
"maxPages": 500,
"checkExternalLinks": true,
"screenshotBrokenPages": true
}

Output

Each checked link produces one dataset record:

FieldDescription
urlThe URL that was checked
statusCodeHTTP status code (200, 404, 500, etc.). 0 = request failed before a response
statusCategory: OK, REDIRECT, BROKEN, BLOCKED, TIMEOUT, ERROR, SERVER_ERROR, CLIENT_ERROR, CRAWL_FAILED, UNKNOWN
confidenceConfidence in the status: high, medium, low
isBrokenDefinitive broken flag (true only for links judged actually broken)
typeLink type: internal, external
foundOnPageThe page the link was found on
anchorTextThe link's anchor text, image alt text, or resource marker
responseTimeResponse time in milliseconds (null if the page could not be crawled)
redirectChainFull redirect path joined by โ†’ if redirected, otherwise null
screenshotUrlKey-value store URL of the broken-page screenshot (if captured)
errorError message if the request failed
warningWarnings (mixed content, slow response, long redirect chain, bot-protection notice)
checkedAtISO-8601 timestamp of when the URL was checked

Status Categories

StatusMeaningIs Broken?
OKLink works (200โ€“299)No
REDIRECTLink redirects (300โ€“399)No
BROKENLink is dead (404, 410)Yes
BLOCKEDAccess denied (401/403) โ€” often bot protectionNo (external / widespread internal) ยท Yes (isolated internal)
TIMEOUTRequest timed outYes
ERRORConnection / DNS failureYes
SERVER_ERRORServer error (500โ€“599)Yes
CLIENT_ERROROther 4xx errorsYes
CRAWL_FAILEDA start/queued page could not be crawled after retriesYes
UNKNOWNStatus could not be classifiedNo

Confidence Levels

ConfidenceMeaning
highStatus is definitive (404, 200, server error, etc.)
mediumStatus may vary (a timeout can be temporary)
lowStatus uncertain โ€” external 403s often block bots but work in a browser

Example Output

{
"url": "https://example.com/old-page",
"statusCode": 404,
"status": "BROKEN",
"confidence": "high",
"isBroken": true,
"type": "internal",
"foundOnPage": "https://example.com/blog",
"anchorText": "Read our old article",
"responseTime": 245,
"redirectChain": null,
"screenshotUrl": "https://api.apify.com/v2/key-value-stores/abc123/records/screenshot-xyz",
"error": null,
"warning": null,
"checkedAt": "2025-12-13T10:30:00.000Z"
}
{
"url": "https://www.viator.com/tours",
"statusCode": 403,
"status": "BLOCKED",
"confidence": "low",
"isBroken": false,
"type": "external",
"foundOnPage": "https://yoursite.com/travel",
"warning": "External site may be blocking automated requests. Verify manually.",
"checkedAt": "2025-12-13T10:30:00.000Z"
}

Summary Statistics

After each run, a summary object is saved to the Key-Value Store under the key summary:

{
"totalLinksChecked": 847,
"brokenLinks": 12,
"blockedLinks": 5,
"redirects": 45,
"warnings": 8,
"pagesProcessed": 100,
"scanCompletedAt": "2025-12-13T10:45:00.000Z"
}

The end-of-run log also prints detailed lists of warnings, blocked links, and broken links for quick review.

How to Use the Results

  1. Export to CSV โ€” download the dataset for spreadsheets or client reports.
  2. Filter by status โ€” use dataset filters to show only broken links or only redirects.
  3. Use the isBroken field โ€” filter isBroken: true to get only definitely broken links and ignore bot-blocked externals.
  4. Use screenshots โ€” broken internal-page screenshots are stored in the Key-Value Store; their URLs appear in screenshotUrl.
  5. Automate with schedules โ€” schedule runs, optionally with monitoringMode, to track site health over time.

Integrate via API

const { ApifyClient } = require('apify-client');
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('YOUR_USERNAME/site-health-scanner').call({
startUrls: [{ url: 'https://example.com' }],
maxPages: 500
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
// Only definitely broken links (ignores bot-blocked externals)
const brokenLinks = items.filter(item => item.isBroken === true);
console.log(`Found ${brokenLinks.length} broken links`);

Limitations

  • Crawl scope: Crawling is bounded by maxPages (hard cap 10,000) and maxDepth (max 10). Larger sites are truncated. Only pages on the start URLs' domains are crawled; external links are status-checked but never crawled.
  • Link status checks are HTTP HEAD/GET, not full page loads. Crawled pages are rendered in a real browser (so JavaScript-injected links are found), but each discovered link's status is verified with a HEAD request (GET fallback) โ€” no JavaScript execution or cookies on the link target itself. Very complex SPAs may still hide some links from extraction.
  • Screenshots are narrow by design. A screenshot is only taken for an internal page link that returns a 4xx/5xx error, excluding 401/403 (access-blocked) and excluding non-page resources (images, scripts, stylesheets). External broken links are not screenshotted.
  • Bot-block detection is heuristic. External 401/403s and widespread internal 403 patterns are treated as bot protection (isBroken: false), which can occasionally misclassify a genuinely broken or genuinely blocked page. Verify BLOCKED links manually.
  • Anti-bot / WAF blocking. Sites behind Cloudflare or advanced WAFs may block the scanner entirely even with the residential proxy enabled.
  • No robots.txt enforcement. The crawler does not read or obey robots.txt or crawl-delay directives โ€” only scan sites you own or have permission to scan, and use requestDelay to be polite.
  • No performance/SEO scoring. This actor does not run Lighthouse, does not score performance, and does not analyze SSL certificates, Core Web Vitals, or on-page SEO. It records raw response time and flags responses slower than 3 seconds only.
  • Rate limits. Fast scans of aggressive sites can trigger 429s; raise requestDelay if needed.
  • Redirect depth. Redirect chains are followed up to 10 hops; longer chains are truncated.

Use Cases

  • SEO audit โ€” run before and after migrations to catch links that could hurt rankings.
  • Client reporting โ€” use screenshots to show clients exactly what's broken.
  • Continuous monitoring โ€” schedule runs with monitoringMode to catch newly broken links.
  • Pre-launch QA โ€” verify links before going live.

Troubleshooting

ProblemSolution
Timeout errorsIncrease the timeout setting
Many 403 "blocked" on external sitesExpected โ€” large sites block bots. Verify manually if needed
Missing pagesIncrease maxDepth or maxPages
Slow scansDisable checkExternalLinks or screenshotBrokenPages
Some links not foundComplex JavaScript navigation may hide links from extraction
Everything blockedTry enabling useProxy; the site may have a strong WAF

Changelog

v1.2 (2026)

  • NEW: Regression monitoring mode (monitoringMode) with per-start-URL state and optional webhook alerts (alertWebhookUrl) when new broken links appear.

v1.1.0

  • IMPROVED: Better status classification โ€” BLOCKED (401/403) is separated from BROKEN; external 403s marked isBroken: false, isolated internal 403s marked isBroken: true.
  • NEW: confidence field (high/medium/low) and isBroken field.
  • NEW: Verbose end-of-run log output for warnings, blocked links, and broken links.
  • NEW: blockedLinks count in the summary.

v1.0.0

  • Initial release: broken-link detection with screenshot proof, redirect chain tracking, mixed content warnings, external link checking, response-time flagging.

Find broken links before your users do.