SEO Audit API — On-Page Checks, Meta Tags, Headings & Schema
Pricing
$1.00 / 1,000 pages
SEO Audit API — On-Page Checks, Meta Tags, Headings & Schema
On-page SEO audit of any URL over plain HTTP: title, meta, canonical, robots, hreflang, headings, images alt, links, JSON-LD, redirects, a 0-100 score with issues. SEO Audit API.
Pricing
$1.00 / 1,000 pages
Rating
0.0
(0)
Developer
yestrue
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Give it a list of page addresses and get one clean JSON record per page with everything an on-page SEO check looks at: the redirect chain and final URL, HTTP status, HTTPS and HSTS, title and meta description with their lengths, robots directives (meta and X-Robots-Tag), canonical link and whether it points to the page itself, hreflang set, viewport, language, favicon, H1-H6 counts and texts, Open Graph and Twitter card tags, every JSON-LD type on the page, word count and text-to-HTML ratio, images without alt text, internal/external/nofollow link counts, scripts, stylesheets, iframes and mixed content — plus a 0-100 score with a list of issues, each with a severity, a penalty and a plain-language message. Everything is read over plain HTTP the way a crawler reads it: no browser, no Lighthouse, so a thousand pages take minutes, not hours.
Try it in 1 minute:
- Press Start — the prefilled input audits two pages: Wikipedia's article on search engine optimization and example.com.
- Seconds later the Output tab shows both records with their score, issues, title, meta description, headings, canonical, structured data types and link counts.
- Download JSON, CSV or Excel, or paste your own URLs, switch on Links list / Images list, or audit as a mobile user agent.
Price: $1.00 per 1,000 pages (one page = one record). Pages that answer 404/410, non-HTML answers, pages a robots.txt forbids and inputs that are not addresses are free.
Use it to:
- Audit a sitemap or a list of landing pages on a schedule and alert on score drops, new noindex tags, lost canonicals or broken redirect chains.
- Check a competitor's pages: title and description patterns, heading structure, schema types, word counts, internal linking.
- Feed an AI agent or a reporting pipeline with structured on-page facts instead of raw HTML.
- Verify a site migration: every old URL's status chain, final URL, canonical and hreflang in one table.
- Find pages with missing alt text, mixed content, thin content or missing Open Graph tags across a whole site.
How it works
- Addresses are normalised (https:// is added when missing, #fragments dropped) and deduplicated; one that is not a web address gets a free
INVALID_INPUTrecord. - robots.txt of the page's site is read once per origin (default on). A page it disallows for this Actor gets a free
ROBOTS_DISALLOWEDrecord and is not fetched. - The page is requested over plain HTTP with a desktop Chrome or a mobile Safari user agent, following up to 10 redirects one hop at a time so every hop lands in
statusChain[]. 404/410 → freeNOT_FOUND; a non-HTML answer (PDF, JSON, image…) → freeNOT_HTML; a page bigger than Max page size → freeFAILED. Other 4xx/5xx answers and network errors are tried again (a 429/403 after a wait on the same route, a 5xx or a dropped connection through the proxy) and end as a freeFAILEDrecord with the reason. - The HTML is parsed (no JavaScript is run): meta tags, links, headings, images, scripts, JSON-LD (including
@graphand nested objects), visible text. - The score starts at 100 and loses points per issue found; the rules and penalties are listed under Output. HTTP errors never reach the score — they are their own statuses.
Requests go directly from Apify's servers; the proxy is used only to try again after a network or server error.
Input
| Field | What it does |
|---|---|
urls | Pages to audit, one per line (https:// is added when missing). |
includeLinks | Add links[] — { url, text, rel, internal }, up to 500 (default off). The counts are always there. |
includeImages | Add images[] — { src, alt, width, height }, up to 200 (default off). |
includeHeadings | Add headings[] — { level, text } in page order, up to 200 (default on). |
includeSchema | Add schema[] — the JSON-LD blocks as parsed objects, first 50 KB (default on). jsonLdTypes[] is always there. |
respectRobots | Skip pages the site's robots.txt disallows (default on). |
userAgent | desktop (Chrome on Windows, default) or mobile (Safari on iPhone). |
maxBytes | Pages with bigger HTML are not read (default 3 MB). |
maxConcurrency | Pages fetched at the same time (default 5). |
proxyConfiguration | Used only to try again after a network or server error; the first try is direct. |
Output
One record per page:
| Field | What it is |
|---|---|
url, httpStatus, statusChain[], redirectCount | The final address, its status, every hop { url, status } and how many redirects were followed |
contentType, pageSizeBytes, responseTimeMs, isHttps, hsts, xRobotsTag | Fetch facts (size after decompression; time including redirects and download) |
title, titleLength, metaDescription, metaDescriptionLength | The texts and their lengths in characters |
metaRobots, noindex, nofollow | The robots meta tag, and whether the meta tags or the X-Robots-Tag header say noindex / nofollow |
canonical, canonicalIsSelf, hreflang[], hreflangHasXDefault | Canonical link (absolute) and whether it is the page itself; { lang, href } alternates |
lang, charset, hasViewport, viewportContent, favicon | Document facts |
h1[], h1Count … h6Count, headings[] | Heading texts and counts |
og, twitter | { title, description, image, type, url, siteName } and { card, title, description, image }; null when the page has none |
jsonLdTypes[], hasStructuredData, schema[] | Every @type found (also inside @graph and nested objects); JSON-LD, microdata or RDFa present; the parsed JSON-LD |
wordCount, textToHtmlRatio | Visible words (scripts and styles stripped) and text length ÷ HTML length |
imagesTotal, imagesWithoutAlt, imagesWithoutAltSrcs[], images[] | Image counts, the sources without alt text (up to 50), the list |
internalLinksCount, externalLinksCount, nofollowLinksCount, links[] | Links classified against the final URL — www. variants are the same site, other subdomains are external |
hasAmpLink, hasRssFeed, inlineScriptsCount, externalScriptsCount, stylesheetsCount, iframesCount, mixedContent | Resources; mixedContent counts http:// resources on an HTTPS page |
score, issues[], issuesCount | 0-100, { id, severity, penalty, message } and { error, warning, notice } |
input, status, error, scrapedAt | What you gave, OK / NOT_FOUND / NOT_HTML / ROBOTS_DISALLOWED / INVALID_INPUT / FAILED, the reason, and when |
The rules (score = 100 − penalties, never below 0):
| Issue | Severity | Penalty |
|---|---|---|
missing-title / title-too-long (> 60) / title-too-short (< 20) | error / warning / notice | 15 / 5 / 2 |
missing-meta-description / meta-description-too-long (> 160) / meta-description-too-short (< 70) | error / warning / notice | 10 / 3 / 2 |
missing-h1 / multiple-h1 | error / warning | 10 / 5 |
missing-canonical / canonical-elsewhere | warning / notice | 5 / 2 |
noindex | error | 20 |
missing-viewport / missing-lang | error / notice | 10 / 2 |
images-without-alt | warning | 1 per image, at most 10 |
no-structured-data / no-open-graph | notice | 3 / 3 |
not-https / mixed-content | error / warning | 10 / 5 |
redirect-chain (2 or more redirects) / page-too-big (> 2 MB) | notice / warning | 2 / 5 |
thin-content (< 300 words) / low-text-ratio (< 10 %) | notice | 3 / 2 |
hreflang-no-x-default | notice | 1 |
Example
{"input": "example.com/guide/ranking","url": "https://www.example.com/guide/ranking/","httpStatus": 200,"statusChain": [{ "url": "https://example.com/guide/ranking", "status": 301 },{ "url": "https://www.example.com/guide/ranking/", "status": 200 }],"redirectCount": 1,"contentType": "text/html; charset=utf-8","pageSizeBytes": 6505,"responseTimeMs": 212,"isHttps": true,"hsts": true,"xRobotsTag": null,"title": "How Search Engines Rank Pages — A Field Guide","titleLength": 45,"metaDescription": "A practical guide to how search engines crawl, index and rank web pages, with the on-page signals that matter most and how to check them.","metaDescriptionLength": 137,"metaRobots": "index, follow, max-snippet:-1","noindex": false,"nofollow": false,"canonical": "https://www.example.com/guide/ranking/","canonicalIsSelf": true,"hreflang": [{ "lang": "en", "href": "https://www.example.com/guide/ranking/" },{ "lang": "de", "href": "https://www.example.com/de/guide/ranking/" },{ "lang": "x-default", "href": "https://www.example.com/guide/ranking/" }],"hreflangHasXDefault": true,"lang": "en","charset": "utf-8","hasViewport": true,"viewportContent": "width=device-width, initial-scale=1","favicon": "https://www.example.com/static/favicon.ico","h1": ["How search engines rank pages"],"h1Count": 1,"h2Count": 3,"h3Count": 2,"h4Count": 0,"h5Count": 0,"h6Count": 0,"headings": [{ "level": 1, "text": "How search engines rank pages" },{ "level": 2, "text": "Crawling and indexing" },{ "level": 2, "text": "Ranking signals on the page" }],"og": { "title": "How Search Engines Rank Pages", "description": "A practical field guide to ranking signals.", "image": "https://www.example.com/static/ranking-cover.png", "type": "article", "url": "https://www.example.com/guide/ranking/", "siteName": "Example Guides" },"twitter": { "card": "summary_large_image", "title": "How Search Engines Rank Pages", "description": "A practical field guide to ranking signals.", "image": "https://www.example.com/static/ranking-cover.png" },"jsonLdTypes": ["Article", "Organization"],"hasStructuredData": true,"schema": [{ "@context": "https://schema.org", "@type": "Article", "headline": "How Search Engines Rank Pages", "author": { "@type": "Organization", "name": "Example Guides" }, "datePublished": "2026-09-01" }],"wordCount": 536,"textToHtmlRatio": 0.47,"imagesTotal": 2,"imagesWithoutAlt": 0,"imagesWithoutAltSrcs": [],"images": [],"internalLinksCount": 6,"externalLinksCount": 3,"nofollowLinksCount": 2,"links": [],"hasAmpLink": true,"hasRssFeed": true,"inlineScriptsCount": 1,"externalScriptsCount": 1,"stylesheetsCount": 1,"iframesCount": 0,"mixedContent": 0,"score": 100,"issues": [],"issuesCount": { "error": 0, "warning": 0, "notice": 0 },"status": "OK","error": null,"scrapedAt": "2026-10-19T12:00:00.000Z"}
A page with problems looks like this in issues[]:
[{ "id": "missing-meta-description", "severity": "error", "penalty": 10, "message": "The page has no meta description." },{ "id": "multiple-h1", "severity": "warning", "penalty": 5, "message": "The page has 3 H1 headings; use one." },{ "id": "images-without-alt", "severity": "warning", "penalty": 3, "message": "3 image(s) have no alt text." },{ "id": "thin-content", "severity": "notice", "penalty": 3, "message": "The page has only 15 words of visible text; aim for 300 or more." }]
FAQ
Is this an official API? No, it's an unofficial API built on public pages — it reads any web page the way a search engine's crawler does and turns what it finds into a stable JSON record. There is no SEO authority behind the score: it is a transparent checklist with fixed penalties, listed above, so two runs on the same HTML always give the same number.
Does it measure Core Web Vitals or Lighthouse scores? No. This Actor reads HTML and headers over plain HTTP; it does not run a browser, so it cannot measure rendering, loading speed or layout shifts. For Lighthouse performance, accessibility, best-practice and SEO scores and the Core Web Vitals of the same URLs, use our PageSpeed Insights API Actor (pagespeed-api) — the two together cover both the content and the speed side of an audit.
What about pages built with JavaScript? The record shows what the server sends, which is also what a crawler sees first. A page that renders its title, headings or links only in the browser will look thin here; that is a real finding (search engines index server-sent HTML first and render later), but check such pages with a browser-based tool too.
Why is a page NOT_FOUND, NOT_HTML or ROBOTS_DISALLOWED? The address answers 404/410 (or its host does not resolve); the answer is not an HTML page (a PDF, an image, JSON); or the site's robots.txt disallows the page for this Actor's user agent — turn Respect robots.txt off only for sites you are allowed to read regardless. None of these is charged.
Can it crawl a whole site? Not in this version: it audits the addresses you give it. Paste the URLs of a sitemap, or combine it with a sitemap or crawler Actor whose output feeds this one.
Like it? A short review on the Store page helps other people find this Actor. Something missing or broken? Tell us on the Issues tab — we read every one.
Related Actors
- Sitemap Extractor — every URL, date and image from a site's sitemaps
- Website Tech Stack Detector — CMS, frameworks and analytics of any site
- Domain Checker — WHOIS, DNS and SSL expiry in bulk
- Web Fetch API — any URL as HTML, text, Markdown or JSON
- Website Screenshot API — full page and mobile screenshots