SEO Audit Tool: technical SEO issues per page, in bulk avatar

SEO Audit Tool: technical SEO issues per page, in bulk

Pricing

from $19.50 / 1,000 page auditeds

Go to Apify Store
SEO Audit Tool: technical SEO issues per page, in bulk

SEO Audit Tool: technical SEO issues per page, in bulk

SEO audit tool for up to 500 pages per run: title and description lengths, canonical, robots meta, hreflang, headings, word count, images without alt text, structured data, Open Graph, viewport and redirects, with a ranked list of issues per page. Respects robots.txt.

Pricing

from $19.50 / 1,000 page auditeds

Rating

0.0

(0)

Developer

Steadydata Team

Steadydata Team

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

An SEO audit tool for up to 500 pages per run. Audit the pages on the technical basics and get, per page, a ranked list of what is actually wrong: a forgotten noindex, a missing or overlong title, no canonical, no H1 or six of them, images without alt text, thin content, no viewport, no structured data. Plus the measured fields behind every verdict. Pages that their own robots.txt forbids are skipped and never charged.

Why this scraper

  • It tells you what to fix, not just what it found. Thirty columns of raw fields are a dataset; issues is a to-do list. Every failed check comes back with a code, a severity (critical, warning, info), a sentence and the measured value, sorted worst first. Filter on severity = critical and you have your morning.
  • The noindex check alone pays for a run. A page nobody can find because a staging noindex shipped to production is the most expensive SEO mistake there is, and it is invisible in a browser.
  • It respects robots.txt. The target's own robots.txt is read first, with the rules search engines actually use, and a page it forbids comes back as a free ROBOTS_DISALLOWED record. An audit that reports on robots meta tags while ignoring robots.txt would not be worth much.
  • Your limits, not ours. Title and description length limits are inputs, so the audit matches your own house style instead of a number we picked.
  • Sitemaps found for free. Every row lists the sitemaps declared in the site's robots.txt, which is usually the fastest way to find the rest of the pages.
  • Only delivered pages are charged. Robots refusals, dead URLs, timeouts and bot walls are free error records. No start fee.

Who this is for

SEO teams and agencies auditing a site or a portfolio of client sites on a schedule. Developers checking a release for the mistakes that only show up in the HTML. Anyone who needs the technical basics of hundreds of pages in one table.

Who this is not for

Read this before you buy. This actor fetches the HTML of each URL you give it and does not run JavaScript, so a page that renders its title and content in the browser shows up emptier here than it looks to a visitor. It also audits only the URLs you supply: it does not crawl a site to discover pages (the sitemapsInRobots field points you at the sitemap so you can feed those URLs in). Page speed, Core Web Vitals and Lighthouse scores are not included; those need a browser and Google's own API.

Input fields

FieldTypeRequired or defaultWhat it does
urlslist of textrequiredPage URLs or domains, one per row, up to 500. A domain is audited on its home page. A URL that its own robots.txt forbids is skipped and not charged.
languagetexten-US,en;q=0.9Language header sent with the request.
titleMaxLengthnumber60A title longer than this is reported as an issue. Google truncates around 60 characters.
descriptionMaxLengthnumber160A meta description longer than this is reported as an issue. Google truncates around 160.

Input example

{
"urls": ["https://apify.com", "wordpress.org", "https://example.com/product/x"],
"language": "en-US,en;q=0.9",
"titleMaxLength": 60,
"descriptionMaxLength": 160
}

A domain is audited on its home page; a full URL is audited as given.

Output example

FieldTypeWhat it holdsExample
urltextThe page that was audited.https://wordpress.org
finalUrltextThe page that was really read, after every redirect.https://wordpress.org/
statusCodenumberThe HTTP status the page answered with; anything but 200 means a visitor sees something else.200
issueCountnumberHow many findings this row holds; zero means nothing went wrong.1
issueslistThe findings themselves, each with what is wrong and how serious it is.[{"code": "images-without-alt", "severity": "warning", "m...
titletextThe page title as it stands in the HTML, which is what a search result shows.Blog Tool, Publishing Platform, and CMS – WordPress.org
titleLengthnumberCounted from title; empty when the page has no title.55
metaDescriptiontextFilled when the page has a meta description; missing is reported as an issue.Open source software which you can use to easily create a...
metaDescriptionLengthnumberCounted from metaDescription; empty when the page has none.90
canonicaltextFilled when the page declares a canonical link.https://wordpress.org/
robotsMetatextFilled when the page has a robots meta tag; without one the page is indexable by default.max-image-preview:large
isIndexabletrue/falseTrue when nothing stops a search engine from indexing the page; false means it will never rank.True
h1listThe first H1 on the page; empty for a page without one, which is reported as an issue.["Meet WordPress"]
h1CountnumberHow many h1 headings the page has; none and more than one are both a finding.1
h2CountnumberHow many h2 headings the page has, a rough measure of its structure.10
wordCountnumberHow many words of text the page holds; a thin page rarely ranks.524
imageCountnumberHow many images the page carries.18
imagesWithoutAltnumberHow many of those carry no alt text, which costs both accessibility and image search.9
internalLinksnumberHow many links point to the site's own pages.61
externalLinksnumberHow many point elsewhere.18
hreflanglistFilled when the page declares language alternatives.["af-za", "am-et"]
structuredDataTypeslistThe schema.org types in the JSON-LD on the page; empty when the page has none.["ImageObject", "Organization"]
openGraphlistThe Open Graph tags that decide how the page looks when shared; empty when the page has none.["description", "image"]
hasTwitterCardtrue/falseWhether the page declares Twitter Card tags.True
hasViewporttrue/falseWhether the page declares a viewport, the tag that makes it usable on a phone.True
htmlLangtextFilled when the html tag declares a language.en-US
charsettextFilled when the page declares a character set.utf-8
isHttpstrue/falseTrue when the page is served over https.True
redirectedtrue/falseTrue when the final URL differs from the one submitted.False
contentEncodingtextFilled when the server compressed the response, for example gzip or br.br
pageSizeBytesnumberThe size of the page in bytes; a heavy page is slow on a phone.165017
responseTimeMsnumberHow long the server took to answer, in milliseconds.37
robotsRuletextThe robots.txt rule that decided whether this URL could be fetched, so the choice can be checked.geen regel
sitemapsInRobotslistThe sitemaps named in robots.txt; empty when robots.txt names none or does not exist.["https://wordpress.org/sitemap.xml", "https://wordpress....
{
"url": "https://example.com/pagina",
"finalUrl": "https://example.com/pagina",
"statusCode": 200,
"issueCount": 3,
"issues": [
{"code": "noindex", "severity": "critical", "message": "The page tells search engines not to index it", "value": "noindex, nofollow"},
{"code": "canonical-missing", "severity": "warning", "message": "No canonical link", "value": null},
{"code": "images-without-alt", "severity": "warning", "message": "4 of 9 images have no alt text", "value": 4}
],
"title": "Een nette titel voor deze pagina",
"titleLength": 32,
"metaDescription": "Een nette beschrijving van deze pagina.",
"metaDescriptionLength": 39,
"canonical": null,
"robotsMeta": "noindex, nofollow",
"isIndexable": false,
"h1": ["Kop een"],
"h1Count": 1,
"h2Count": 2,
"wordCount": 251,
"imageCount": 9,
"imagesWithoutAlt": 4,
"internalLinks": 24,
"externalLinks": 3,
"hreflang": ["en", "nl"],
"structuredDataTypes": ["Organization", "PostalAddress"],
"openGraph": ["image", "title"],
"hasTwitterCard": true,
"hasViewport": true,
"htmlLang": "nl",
"charset": "utf-8",
"isHttps": true,
"redirected": false,
"contentEncoding": "br",
"pageSizeBytes": 81234,
"responseTimeMs": 412,
"robotsRule": "allow /",
"sitemapsInRobots": ["https://example.com/sitemap.xml"],
"status": "ok"
}

A page its own robots.txt forbids produces an error record instead, and is not charged:

{
"input": "https://example.com/private/page",
"status": "error",
"errorCode": "ROBOTS_DISALLOWED",
"error": "https://example.com/private/page is forbidden by its own robots.txt (disallow /private); skipped and not charged"
}

Error codes: INVALID_URL, ROBOTS_DISALLOWED, FETCH_FAILED, BLOCKED. INPUT_TRUNCATED appears once when your input is longer than this actor accepts.

Pricing

Pay per event: one page-audited event per delivered page. No charge for robots refusals, dead URLs, timeouts or bot walls, and no start fee.

Free Apify plan: this actor delivers up to 25 rows per run for accounts on the Apify free plan, and then stops with a message. That limit is set by us, not by Apify. It exists so the actor keeps paying for itself for the people who do pay. Any paid Apify plan runs it at full size, billed per delivered row, with failed rows never charged.

Reviews: if this actor saves you time, a short review on this page is the one thing that helps most. Ratings are what other buyers look at first, and we have no other way to ask.

FAQ

Which issues can it report? noindex, http-error, title-missing, title-too-long, title-too-short, description-missing, description-too-long, canonical-missing, h1-missing, h1-multiple, images-without-alt, thin-content, viewport-missing, lang-missing, structured-data-missing, open-graph-missing and no-https.

Why does imagesWithoutAlt count images with alt=""? Because an empty alt is only correct for decorative images, and at scale it is far more often an oversight. The count and the total are both in the row, so you can judge.

Why does one page report 117 images without alt text? Because that is what the HTML as served contains. Sites that lazy-load images often set the alt attribute later in JavaScript, which this actor does not run, so treat a very high count on an image-heavy page as "check this by hand" rather than as 117 separate mistakes. The total count is in the row so you can see the ratio.

Are 251 words really thin content? The thin-content issue is info, not a verdict: for a product page it is normal, for an article it is a signal. Filter it out if it does not apply to your pages.

Can it crawl my whole site? No. It audits the URLs you give it. Use sitemapsInRobots from the first run to collect the rest of your URLs, then feed those in.

Is personal data collected? No. These are properties of a web page.

What happens when a check is wrong? Every issue carries the measured value, so a disagreement is verifiable rather than a matter of opinion. If a rule is genuinely wrong, it gets fixed.