Website SEO Audit & Broken Link Checker
Pricing
from $1.00 / 1,000 page auditeds
Website SEO Audit & Broken Link Checker
Crawl a website and audit every page's on-page SEO (title, meta description, headings, canonical, robots, hreflang, Open Graph, JSON-LD, alt text, word count) and check every link for broken pages and redirects. Obeys robots.txt.
Pricing
from $1.00 / 1,000 page auditeds
Rating
0.0
(0)
Developer
iCyan Studio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 days ago
Last modified
Categories
Security
Limited permissionsShare
Crawl your website, audit every page's on-page SEO and check every link for breakage in one run. Enter a domain and the actor crawls it breadth-first inside the site, obeying robots.txt. It also reads your sitemap, so pages that nothing links to are audited too. For each HTML page you get the title, meta description, headings, canonical, robots directives, hreflang, Open Graph, JSON-LD types, image alt text, word count and response time, plus a list of SEO issues with plain-English messages. Every link on those pages is checked once per run, and each page lists its broken links with their anchor text.
One page or link failing never fails the run. A page that returns 500 is reported as an http-error, and a link that times out is reported as broken with the reason. Results come as JSON, CSV or Excel, over the API, or from AI agents through the Apify MCP server.
What it does
- Crawls your site politely: breadth-first from the start URL, same origin by default (or the whole registrable domain with
includeSubdomains), up tomaxPagespages andmaxDepthclicks deep. A start URL that redirects (example.comtowww.example.com/en) brings the target host into scope automatically. It obeysrobots.txtand itsCrawl-delay, and never sends more than 2 requests at once to one host. - Audits on-page SEO: title and meta description (with lengths),
h1texts andh2count, canonical (and whether it points at the page itself), robots meta andX-Robots-Tag, anisIndexableverdict,html lang, hreflang alternates, Open Graph, Twitter card, JSON-LD@types (including@graph), visible word count, and images withoutalt. - Flags issues with stable codes:
title-missing,title-too-long(> 60),meta-description-missing,meta-description-too-long(> 160),h1-missing,h1-multiple,canonical-invalid,canonical-mismatch,noindex,images-missing-alt,broken-links,thin-content(< 200 words),slow-response(> 3 s),http-error,html-truncated. - Checks every link once: HEAD first, GET when a server rejects HEAD. It follows up to 10 redirects and retries 429 and 5xx with backoff. Each unique link is checked once per run and the result is reused on every page that links to it. Links to pages the crawler fetches anyway are not checked a second time, and they cost nothing extra.
mailto:,tel:andjavascript:links are skipped. - Handles real-world HTML: broken markup,
<base href>, Shift_JIS and other declared charsets, and Chinese and Japanese text (each character counts as a word, so CJK pages aren't all flagged as thin).
Use cases
- Technical SEO audits: find missing or too-long titles and descriptions, missing
h1s,noindexpages and wrong canonicals across a whole site, then sort the Issues view. - Broken link monitoring: schedule a weekly run and send pages with
broken-linksto Slack, email or a webhook through Apify integrations. - Site migrations: after a relaunch, see which internal links now redirect or return 404, and on which pages they sit.
- International SEO: check
html langand hreflang alternates on every language version. - Content quality: find thin pages and images without alt text.
- AI agents (Apify MCP, LangChain, OpenAI Agents): a small input and a flat, documented output. An agent can ask "which pages on example.com have SEO problems?" and get structured answers.
Input
| Field | Type | Default | Description |
|---|---|---|---|
startUrls | string[] | ["https://crawlee.dev"] | Where the crawl starts: a domain (example.com) or a page URL. Up to 100. Start URLs are always audited. |
maxPages | integer 1–10,000 | 10 | Stop after auditing this many HTML pages. This also caps the cost. Set it higher for a full-site audit. |
maxDepth | integer 0–50 | 5 | Clicks from the start URL. 0 audits only the start URLs. Sitemap pages count as depth 1. |
checkLinks | boolean | true | Check every link on the audited pages. |
checkExternalLinks | boolean | true | Also check links to other websites. |
includeSubdomains | boolean | false | Crawl every host of the same registrable domain (www., blog., the bare domain), on http and https. |
urlPattern | regex | none | Crawl only pages whose URL matches (case-insensitive). Links to other pages are still checked. |
excludeUrlPattern | regex | none | Don't crawl pages whose URL matches. Links to them are still checked. |
respectRobotsTxt | boolean | true | Obey robots.txt rules and Crawl-delay (capped at 10 s). Turn it off only for a site you own. |
useSitemap | boolean | true | Also queue pages from the sitemap (found through robots.txt, or /sitemap.xml). |
concurrency | integer 1–20 | 5 | Requests at the same time across all hosts. One host never gets more than 2 at once. |
Example: audit the blog only, without tag and pagination pages:
{"startUrls": ["https://example.com/blog/"],"urlPattern": "/blog/","excludeUrlPattern": "/tag/|\\?page=","maxPages": 500}
Example: a quick on-page audit with no link checks:
{"startUrls": ["https://example.com"],"checkLinks": false,"maxPages": 1000}
Output
One dataset item per audited page. This one is from a real run on www.icyanstudio.net, 2026-10-25 (shortened). That site renders its content in the browser, so the HTML the server sends has no h1 and almost no text:
{"url": "https://www.icyanstudio.net/","finalUrl": "https://www.icyanstudio.net/en","statusCode": 200,"depth": 0,"responseTimeMs": 670,"title": "iCyan Studio","titleLength": 12,"metaDescription": "Welcome to the iCyan studio. This is a personal website and portfolio to showcase my work, skills, and experience. ...","metaDescriptionLength": 290,"h1": [],"h2Count": 0,"canonical": "https://www.icyanstudio.net/en","canonicalRaw": "https://www.icyanstudio.net/en","canonicalMatches": true,"robotsMeta": null,"xRobotsTag": null,"isIndexable": true,"lang": "en","hreflang": [{ "lang": "en", "href": "https://www.icyanstudio.net/en" },{ "lang": "ja", "href": "https://www.icyanstudio.net/ja" },{ "lang": "zh-TW", "href": "https://www.icyanstudio.net/zh_tw" },{ "lang": "x-default", "href": "https://www.icyanstudio.net/en" }],"og": { "title": "iCyan Studio", "description": "Welcome to the iCyan studio. ...", "image": "https://www.icyanstudio.net/images/icyanstudio.jpg" },"twitterCard": "summary_large_image","jsonLdTypes": [],"wordCount": 2,"imagesTotal": 0,"imagesMissingAlt": 0,"internalLinks": 0,"externalLinks": 0,"brokenLinks": [],"redirectedLinks": 0,"linksNotChecked": 0,"issues": [{ "code": "meta-description-too-long", "message": "The meta description is 290 characters; search results show about 160." },{ "code": "h1-missing", "message": "The page has no <h1> heading." },{ "code": "thin-content", "message": "Only 2 words of visible text (under 200)." }],"crawledAt": "2026-10-25T10:31:16.194Z"}
Broken links on a page look like this (from a run on crawlee.dev; Stack Overflow answers automated requests with 403):
"brokenLinks": [{ "url": "https://stackoverflow.com/questions/tagged/crawlee", "statusCode": 403, "error": null, "anchorText": "Stack Overflow" }]
brokenLinkslists links that returned 400 or higher, or failed (DNS, timeout, TLS, too many redirects, refused address).errorsays why. It isnullwhencheckLinksis off.- URL fields (
canonical,hreflang[].href,og.image) only ever hold absolutehttp(s)URLs. A value such asjavascript:...becomesnull; the canonical as written is kept in the plain-textcanonicalRaw, and an unusable one is reported ascanonical-invalid. canonicalMatchesisnullwhen the page has no usable canonical. A trailing-slash difference (/pythonvs/python/) counts as a mismatch, as it does for search engines.isIndexableistruewhen the status is 2xx, neither the robots meta norX-Robots-Tagsaysnoindex, and the canonical doesn't point elsewhere.linksNotCheckedcounts links that weren't checked: external links withcheckExternalLinksoff, linksrobots.txtdisallows, and links beyond the run's maximum cost.
The dataset has three views: Overview, Issues and Broken links.
Run summary (key-value store OUTPUT)
Every run saves a summary: pages audited and why others weren't (not HTML, failed, redirected off the site, duplicate), crawl candidates skipped (robots.txt, patterns, depth), issue counts by code, link totals (unique, internal, external, checked, answered by the crawl itself), every broken link once with the pages it was found on (up to 1,000, with a total count), robots.txt and sitemap details, and warnings. Example (shortened, from crawlee.dev with maxPages: 3):
{"pagesAudited": 3,"issuesByCode": { "broken-links": 3, "canonical-mismatch": 1 },"links": { "unique": 43, "internal": 29, "external": 14, "checkedByRequest": 40, "resolvedFromCrawl": 3 },"linkChecksCharged": 40,"brokenLinksTotal": 2,"redirectedLinksTotal": 2,"brokenLinks": [{ "url": "https://stackoverflow.com/questions/tagged/crawlee", "statusCode": 403, "error": null, "foundOn": ["https://crawlee.dev/", "https://crawlee.dev/js"], "foundOnCount": 2 }],"stoppedAtMaxPages": true,"warnings": ["Reached maxPages (3); 26 more queued page(s) were not crawled."]}
Pricing
Pay per event: you pay for audited pages and for link checks, nothing else.
| Event | Free plan | Bronze | Silver | Gold |
|---|---|---|---|---|
| Page audited (one HTML page with all its SEO fields and issues) | $0.003 | $0.002 | $0.0015 | $0.001 |
| Link checked (one unique link actually requested, once per run) | $0.0005 | $0.0005 | $0.0005 | $0.0005 |
| Actor start | Apify's default |
For example, a 100-page site with 300 unique links that aren't pages of the site costs $0.30 for the pages plus $0.15 for the links: $0.45 on the free plan. That's much cheaper per page than typical SEO audit actors, and it includes a link check for every link. Links to pages the crawler audits anyway are free. mailto:, tel: and javascript: links, links robots.txt disallows and addresses refused by the safety check are never charged.
Set a maximum cost per run in Apify Console. Every page and every link check is paid for out of that limit before it happens, so the run stops cleanly at the limit and is never cut off halfway. Link checks count toward the limit too, so a tight limit on a link-heavy site audits fewer pages. Turn off checkExternalLinks or checkLinks to spend it all on pages.
FAQ
Does it render JavaScript? No. It audits the HTML your server sends, like most SEO crawlers in their default mode. If your site builds its content in the browser (as the example above does), the audit shows what a non-rendering crawler sees: few words, no h1 and few links. Search engines that render JavaScript may see more.
Why is a link to LinkedIn, Stack Overflow or a Cloudflare-protected site "broken"? Some sites answer every automated request with 403, 429 or 999. Those links show up as broken with the real status code. The actor doesn't try to get around bot protection.
Does it obey robots.txt? Yes, by default. It reads robots.txt for each site it crawls, uses the rules for its own User-Agent (SeoAuditCrawler) or else the * group, and honours Crawl-delay up to 10 seconds. If robots.txt can't be read because of a server error, the site is treated as fully disallowed (RFC 9309), and the run says so. Links to other websites get a single status check and their robots.txt isn't read, as in other link checkers.
What counts as one page? One HTML page that was audited, i.e. one dataset item. A URL that redirects to a page already audited, or that turns out to be a PDF or an image, is not a page. It costs one link check if it was requested.
Why does a run fail? Only when the input is invalid, or when no start URL could be fetched as an HTML page (it's down, it isn't HTML, or robots.txt blocks it). The status message and OUTPUT say which.
Can I audit a site I don't own? Only crawl sites you own or have permission to audit. Requests go out at a polite rate, but the target site's terms are your responsibility.
Limits and notes
- Up to 10,000 pages per run, 10,000 links per page, 5 MB of HTML per page (larger pages are audited up to 5 MB and flagged
html-truncated), 50 sitemap files per run. - One host gets at most 2 requests at a time, so a single site is crawled 2 pages at a time. In a test run, 10 pages of a small site were audited in about 2 seconds.
- For safety, the actor only requests public
httpandhttpsaddresses. Links and redirects that point at private, loopback or cloud-metadata addresses are refused, reported as broken with the reason, and not charged when no request was sent. - Links are taken from
<a href>. Images, scripts and stylesheets are counted or ignored, not checked.
Feedback
Found a page it audits wrongly, or need a field or an issue rule? Open an issue on the actor's Issues tab with the URL.