SEO Audit & Broken Link Checker: Site Crawler
Pricing
$2.00 / 1,000 page auditeds
SEO Audit & Broken Link Checker: Site Crawler
Crawl a website and get a per-page SEO audit plus every broken link, redirect and blocked URL: titles, meta, H1, alt, canonicals, duplicates, sitemap & robots checks.
Pricing
$2.00 / 1,000 page auditeds
Rating
0.0
(0)
Developer
vantum
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
5 days ago
Last modified
Categories
Security
Limited permissionsShare
SEO Audit & Broken Link Checker
Crawl any website and get, in one run:
- a technical SEO audit of every page: title, meta description, H1, image alt text, canonical, robots/noindex, hreflang, lang, viewport, thin and duplicate content, mixed content, slow and heavy pages, each with a 0–100 score;
- every broken link, broken image and broken script/stylesheet, with the pages that link to it and the anchor text;
- every internal redirect and redirect chain, so you can update links to point at the final URL;
- a site summary: issues ranked by how many pages they affect, duplicate title/description/content groups, sitemap quality (broken, redirecting, noindex and orphan URLs), robots.txt status and HTTP status codes.
Plain HTTP and no browser, so it is fast and cheap. $2 per 1,000 audited pages. Link checks, link rows, summaries, dry runs and failed sites are free.
Why this one
| This Actor | Typical SEO audit actors | Typical broken-link checkers | |
|---|---|---|---|
| Price | $0.002 per page, no start fee | $0.005–$0.04 per page | $0.001 per link checked, often plus a start fee |
| Per-page SEO audit (44 checks) | ✅ | ✅ | ❌ |
| Broken links with source page and anchor text | ✅ free | partial | ✅ (paid per link) |
| External links, images, scripts and CSS checked | ✅ free | rarely | some |
| HEAD first, then GET fallback (no false "405 broken") | ✅ | ❌ | some |
| 403/429/999 reported as blocked, not broken | ✅ | ❌ | ❌ |
| Canonical target validated (broken, redirect, noindex) | ✅ | ❌ | ❌ |
| Sitemap quality: broken, redirecting, noindex and orphan URLs | ✅ | rarely | ❌ |
| Duplicate titles, descriptions and content, grouped | ✅ | partial | ❌ |
| Respects robots.txt | ✅ (option) | varies | varies |
| Free dry run with a cost estimate | ✅ | ❌ | ❌ |
| Stops safely before the run timeout and still reports | ✅ | ❌ | ❌ |
Quick start
- Enter one or more websites, e.g.
https://crawlee.dev. - Set Max pages per site (default 100). Not sure how big the site is? Tick Dry run first: it is free and tells you how many pages the sitemap lists and what a full audit would cost.
- Run. Open the Pages overview, Broken links & redirects and Site summary tabs in the output.
{"startUrls": [{ "url": "https://crawlee.dev" }],"maxPagesPerSite": 200,"checkExternalLinks": true,"linkRows": "brokenAndInternalRedirects"}
How it crawls
- It starts at your URL, reads
robots.txtand the sitemaps (including sitemap indexes and.xml.gzfiles), then follows links breadth-first: closest pages first. - It stays on the same site.
www.and the bare domain count as one site. Subdomains are optional. - It audits HTML pages that answer 200. Redirecting, broken and non-HTML URLs are recorded as link results and are not charged.
- Tracking parameters (
utm_*,gclid,fbclid…) are always stripped. Ignore query strings also merges?sort=style variants. - Only crawl / never crawl patterns take plain text or
/regex/. - After crawling, it checks every unique URL found on the audited pages once. Canonical targets go first, then internal links, internal images/scripts/CSS, external links and external resources. It tries
HEADand falls back toGETwhen a server mishandlesHEAD. It retries429/503, honouringRetry-After. - If the run is about to time out, it stops crawling early and still writes every row and the summary.
Not covered: pages that render their content only with JavaScript. These are flagged JS_RENDERED so you know. Also not covered: Core Web Vitals, and pages behind a login.
Output
All rows go to one dataset. rowType tells them apart, and the tabs filter the columns.
page row (charged, one per audited page)
{"rowType": "page","url": "https://crawlee.dev/blog/page/2","statusCode": 200,"score": 86,"errors": 0, "warnings": 2, "notices": 2,"issueCodes": ["TITLE_TOO_LONG", "TITLE_DUPLICATE", "META_DESCRIPTION_DUPLICATE", "CANONICAL_MISSING"],"issues": [{ "code": "TITLE_TOO_LONG", "severity": "warning", "message": "Title is 108 characters (over 60); search engines will cut it.", "count": 108 }],"title": "Crawlee Blog - learn how to build better scrapers | Crawlee …","titleLength": 108,"metaDescriptionLength": 52,"h1": "Crawlee Blog","h1Count": 1,"canonical": null,"canonicalStatus": "missing","indexable": true,"inlinks": 3,"inSitemap": true,"brokenLinkCount": 0,"brokenLinks": null,"linksToRedirects": 0,"imagesMissingAlt": 0,"wordCount": 1210,"responseTimeMs": 182}
Page rows also include: finalUrl, redirectChain, depth, foundOn, notIndexableReason, metaDescription, h2Count, metaRobots, xRobotsTag, lang, hreflang, images, imagesMissingAltSample, internalLinks, externalLinks, structuredDataTypes, ogTitle, ogImage, htmlBytes, contentType and crawledAt.
link row (free, one per problem URL)
{"rowType": "link","url": "https://crawlee.dev/blog/img/lukasp.webp","state": "broken","statusCode": 404,"internal": true,"kind": "img","linkedFromCount": 1,"linkedFrom": [{ "page": "https://crawlee.dev/blog/…", "anchor": null, "kind": "img" }],"checkMethod": "GET"}
state takes one of these values:
broken: 4xx/5xx, a redirect loop or too many redirects.error: DNS, connection, SSL or timeout failure.blocked: 401/403/429/999, meaning the server refused the checker. LinkedIn, Stack Overflow and Cloudflare-protected sites often do this, so check these by hand; they are not counted as broken.redirect: the URL works but redirects; the row shows the fullredirectChain.
Link rows decides which states get rows.
summary row (free, one per site)
The summary row holds:
pagesAudited,averageScore,pagesWithErrors,nonIndexablePages;brokenInternalUrls,brokenExternalUrls,blockedUrls,redirectingInternalUrls,linksChecked,urlsDiscovered;issues: every issue code with the number of pages it affects, worst first;duplicateTitles,duplicateMetaDescriptions,duplicateContent: groups of pages;sitemaps: URLs listed, plus broken, redirecting, noindex and orphan URLs (in the sitemap but linked from no audited page, checked only when the whole site was crawled);robotsTxt,statusCodes,stoppedBy(maxPages,costLimit,timeoutor null).
The same summaries are saved as JSON in the key-value store record RUN_SUMMARY.
Issue codes
Score = 100 − 10 per error − 4 per warning − 1 per notice (minimum 0).
| Code | Severity | Meaning |
|---|---|---|
TITLE_MISSING | error | The page has no <title>. |
TITLE_EMPTY | error | The <title> is empty. |
TITLE_MULTIPLE | warning | The page has N <title> tags. |
TITLE_TOO_LONG | warning | Title is N characters (over 60/160); search engines will cut it. |
TITLE_TOO_SHORT | notice | Title is only N characters (under 30/70). |
TITLE_DUPLICATE | warning | Same title as N other page(s). |
META_DESCRIPTION_MISSING | warning | No meta description. |
META_DESCRIPTION_EMPTY | warning | The meta description is empty. |
META_DESCRIPTION_MULTIPLE | warning | The page has N meta descriptions. |
META_DESCRIPTION_TOO_LONG | notice | Meta description is N characters (over 60/160). |
META_DESCRIPTION_TOO_SHORT | notice | Meta description is only N characters (under 30/70). |
META_DESCRIPTION_DUPLICATE | notice | Same meta description as N other page(s). |
H1_MISSING | warning | No <h1> heading. |
H1_EMPTY | warning | The <h1> is empty. |
H1_MULTIPLE | notice | N <h1> headings. |
IMG_ALT_MISSING | warning | N image(s) without an alt attribute. |
CANONICAL_MISSING | notice | No canonical link. |
CANONICAL_MULTIPLE | error | N canonical links with different targets; search engines may ignore all of them. |
CANONICAL_RELATIVE | notice | Canonical uses a relative URL (…). |
CANONICAL_INVALID | error | Canonical is not a valid http(s) URL (…). |
CANONICAL_OTHER_DOMAIN | warning | Canonical points to another domain (…). |
CANONICAL_TO_OTHER_PAGE | notice | Canonical points to a different page (…); this page will not be indexed itself. |
CANONICAL_INSECURE | warning | Canonical uses http:// on an https:// page. |
CANONICAL_TARGET_BROKEN | error | Canonical target returns …. |
CANONICAL_TARGET_REDIRECTS | warning | Canonical target redirects to …. |
CANONICAL_TARGET_NOINDEX | error | Canonical target is noindex. |
CANONICAL_ON_NOINDEX | notice | Page is noindex but also has a canonical to another page (mixed signals). |
NOINDEX | notice | Page is noindex (…). |
NOFOLLOW | notice | Page is nofollow (…). |
BROKEN_LINKS | error | N broken link(s) on this page. |
BROKEN_IMAGES | warning | N broken image(s) on this page. |
BROKEN_RESOURCES | warning | N broken script, stylesheet or frame URL(s) on this page. |
LINKS_TO_REDIRECTS | notice | N internal link(s) go through a redirect. |
PAGE_REDIRECTED | notice | The URL redirects (…). |
REDIRECT_CHAIN | warning | The URL goes through N redirects. |
LANG_MISSING | notice | No lang attribute on <html>. |
VIEWPORT_MISSING | warning | No mobile viewport meta tag. |
THIN_CONTENT | notice | Only N words of text. |
CONTENT_DUPLICATE | warning | Exactly the same text as N other page(s). |
MIXED_CONTENT | warning | N resource(s) loaded over http:// on an https:// page. |
SLOW_RESPONSE | notice | Server took N ms to respond. |
LARGE_PAGE | notice | HTML is N KB. |
JS_RENDERED | notice | Very little text in the HTML; the page is probably rendered by JavaScript, which this audit does not run. |
HREFLANG_INVALID | warning | Invalid hreflang value(s): …. |
Thresholds: title 30–60 characters, meta description 70–160, thin content under 200 words, slow over 3 s, large HTML over 3 MB. Pages that are noindex or canonicalised elsewhere are left out of duplicate checks.
Pricing
$0.002 per audited page saved ($2 per 1,000). Nothing else is charged:
- link checks and
linkrows: free, however many; summaryrows and theRUN_SUMMARYrecord: free;- dry runs: free;
- sites that fail to open: free;
- broken, redirecting and non-HTML URLs: free (they are not audited pages);
- with Save only pages with issues, pages without issues: free.
Set a maximum cost per run in Apify and the Actor stops before going over it. The summary then shows stoppedBy: "costLimit".
Examples: a 50-page brochure site costs $0.10. A 1,000-page blog costs $2. A 10,000-page shop audited on 2,000 sample pages costs $4.
Tips
- Big sites: run a dry run, then audit a sample with Max pages per site and an Only crawl pattern per section (e.g.
/blog/). - Shops and faceted navigation: tick Ignore query strings. Exclude
?filter=,/tag/and similar. - Fragile servers: lower Max parallel requests to 2–3.
- Only want broken links? Set Link rows to "Broken links only" and Save only pages with issues. Use the Broken links & redirects tab.
- Scheduled monitoring: schedule a weekly run and compare
summaryrows, or connect the dataset to Google Sheets, Slack or Zapier via Apify integrations.
FAQ
Does it render JavaScript? No. It reads the HTML that servers send, which is what most search-engine first passes see. Pages whose text appears only after JavaScript are flagged JS_RENDERED.
Why is a link "blocked" rather than broken? Some sites (LinkedIn returns 999, Stack Overflow 403, many Cloudflare sites 403/429) refuse automated requests even though the link works in a browser. Calling these broken would be wrong, so they are reported separately.
Does it obey robots.txt? Yes, by default, for internal pages. External links are checked with a single request each.
Is it legal? It requests public pages, like a search-engine crawler, at a polite concurrency. Audit sites you own or are allowed to audit.
Support
Open an issue in the Actor's Issues tab with the run ID. Feature requests are welcome.