Page Metadata & SEO Extractor avatar

Page Metadata & SEO Extractor

Pricing

from $4.00 / 1,000 page checkeds

Go to Apify Store
Page Metadata & SEO Extractor

Page Metadata & SEO Extractor

Bulk-extract and check page metadata for up to 500 URLs or an XML sitemap: title, meta description, canonical, robots, Open Graph, X Card, H1, hreflang and image alt text. One row per page with severity-rated issues, plus a run summary.

Pricing

from $4.00 / 1,000 page checkeds

Rating

0.0

(0)

Developer

Cuantic Data

Cuantic Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

7 days ago

Last modified

Categories

Share

Page Metadata & SEO Extractor - Title, Description, Canonical, Robots, H1, Hreflang

Extract and check the classic on-page SEO metadata of a whole list of pages in one run. Paste up to 500 URLs, or give an XML sitemap, and get one dataset row per page with its title, meta description, canonical, robots directives, Open Graph and X/Twitter Card basics, H1 headings, hreflang links and image alt-text counts, plus a list of severity-rated issues (title too long, missing description, canonical that does not match the page, noindex, images without alt, and more). A summary record totals the issues across the run.

Think of it as a bulk meta tag checker, title and meta description length checker, canonical and hreflang audit and H1 / image alt checker in one table, using deterministic checks you can read and reproduce. It uses plain HTTP and an HTML parser: no browser, small and fast.

What this is not. The issues are static checks of the HTML each server returns. They do not predict rankings, indexing or how search results are displayed, and length thresholds are common guidelines rather than search-engine limits (see Where the thresholds come from).

What it does

  • Fetches each page once over plain HTTP(S) (no browser, no JavaScript) and reads the server-returned HTML.
  • Extracts and checks: title, meta description, canonical, robots (meta tag and X-Robots-Tag header), Open Graph and X/Twitter Card (presence of the basics), H1, hreflang and images without alt. Choose which checks to run with the checks input.
  • Reports issues as {check, severity, code, message} (error, warning or info) and counts them per page.
  • Accepts a list of URLs or an XML sitemap (a sitemap index is followed one level deep). Only those URLs are fetched; links found on pages are never followed.
  • Isolates failures: a page that times out, returns 404, is not HTML or points to a refused target gets a row with an error message, and the run continues.
  • Writes a run summary to the key-value store record OUTPUT.

How it relates to the other Cuantic Data SEO actors

  • This actor covers classic page metadata: title, description, canonical, robots, H1, hreflang, image alt text, and whether Open Graph and X Card basics are present.
  • Bulk Rich Results Audit (bulk-rich-results-audit) covers structured data: every JSON-LD block, @type values and parse errors, and the detailed Open Graph and X Card validation (relative or insecure image URLs, conflicting duplicate tags, invalid card types, fallbacks from Open Graph). This actor does not read JSON-LD at all. For Open Graph and X Card it checks only basic presence (a few missing-tag codes such as og-missing, og-title-missing, og-image-missing overlap in meaning with the sibling's), so running both on the same URLs gives mostly complementary results.
  • Bulk PageSpeed Audit (bulk-pagespeed-audit) measures performance, accessibility and SEO scores with Lighthouse for the same kind of URL list.
  • Sitemap URL Extractor (sitemap-url-extractor) is a handy first step to list the URLs of a site that you then feed to this actor.

Who it is for

  • SEO audits and site migrations. Snapshot titles, descriptions, canonicals and hreflang of every page before the switch, run again after, and compare the two datasets.
  • Metadata regression checks. Run the same URL list after each release (for example on an Apify schedule) and watch the OUTPUT totals: a jump in canonical-mismatch or robots-noindex shows up in one number.
  • Content teams. Find pages with missing or oversized titles and descriptions, missing H1s or images without alt text, in one sortable table.
  • Agencies. Hand over a whole-site table instead of pasting URLs one by one into a single-page tool.

Input

FieldTypeDefaultDescription
urlsarray of strings-Pages to check (http/https). Deduplicated (order kept), max 500.
sitemapUrlstring-XML sitemap or sitemap index. Every <loc> page URL is checked, max 500.
checksarray of stringsallAny of title, description, canonical, robots, og, twitter, h1, hreflang, imagesAlt. Empty means all. Fields of unselected checks are null.
titleMinLength / titleMaxLengthinteger50 / 60Characters. Shorter gives an info issue, longer a warning.
descriptionMinLength / descriptionMaxLengthinteger50 / 160Characters. Outside the range gives an info issue.
sameOriginOnlybooleantrueFor sitemaps: keep only child sitemaps and page URLs on the same origin as the sitemap URL.
maxConcurrencyinteger10Pages fetched in parallel, 1 to 10.
timeoutMsinteger20000Timeout per request including redirects, 5000 to 60000 ms.

At least one of urls or sitemapUrl is required. If both are given, urls is used, the sitemap is ignored and a warning is logged.

Example input:

{
"sitemapUrl": "https://www.example-shop.com/sitemap.xml",
"checks": ["title", "description", "canonical", "robots", "h1", "imagesAlt"],
"sameOriginOnly": true
}

Output

One dataset item per URL:

{
"url": "https://www.example-shop.com/products/blue-widget",
"finalUrl": "https://www.example-shop.com/products/blue-widget?ref=nav",
"statusCode": 200,
"redirectCount": 1,
"contentType": "text/html; charset=utf-8",
"checksRun": ["title", "description", "canonical", "robots", "og", "twitter", "h1", "hreflang", "imagesAlt"],
"title": "Blue Widget - Handmade Garden Widgets and Accessories for Every Season | Example Shop",
"titleLength": 85,
"metaDescription": "A very blue widget.",
"metaDescriptionLength": 20,
"canonical": "https://www.example-shop.com/products/blue-widget",
"canonicalMatchesUrl": false,
"robots": { "meta": "index, follow", "xRobotsTag": null, "noindex": false, "nofollow": false },
"openGraph": { "title": "Blue Widget", "description": null, "image": "https://www.example-shop.com/img/widget.jpg", "url": null, "type": "product", "siteName": "Example Shop" },
"twitterCard": { "card": "summary_large_image", "title": null, "description": null, "image": null, "site": "@exampleshop" },
"h1": { "count": 1, "texts": ["Blue Widget"] },
"hreflang": [
{ "hreflang": "en", "href": "https://www.example-shop.com/products/blue-widget" },
{ "hreflang": "es", "href": "https://www.example-shop.com/es/products/blue-widget" }
],
"images": { "total": 6, "missingAlt": 2, "emptyAlt": 1, "missingAltExamples": ["/img/a.jpg", "/img/b.jpg"] },
"issues": [
{ "check": "title", "severity": "warning", "code": "title-too-long", "message": "Title is 85 characters (guideline: 60 or fewer); search results may truncate it." },
{ "check": "canonical", "severity": "warning", "code": "canonical-mismatch", "message": "Canonical (https://www.example-shop.com/products/blue-widget) differs from the page URL (https://www.example-shop.com/products/blue-widget?ref=nav). This is intentional for duplicate or parameterized URLs, but a mistake on an indexable page." },
{ "check": "imagesAlt", "severity": "warning", "code": "image-alt-missing", "message": "2 of 6 image(s) have no alt attribute." },
{ "check": "description", "severity": "info", "code": "description-too-short", "message": "Meta description is 20 characters (guideline: at least 50)." }
],
"issueCounts": { "total": 4, "error": 0, "warning": 3, "info": 1 },
"error": null
}
  • issues holds every issue on the page, errors first. issueCounts totals them by severity.
  • A field is null when its check was not selected, when the page does not have it (for example no <title>), or when the page could not be fetched. Check checksRun to tell the cases apart.
  • canonical is resolved to an absolute URL (against <base href> if present). canonicalMatchesUrl compares it to the final URL after redirects, ignoring the fragment and a trailing slash; query strings count.
  • images.total counts every <img>; missingAlt counts images with no alt attribute (images marked role="presentation", role="none" or aria-hidden="true" are ignored); emptyAlt counts alt="", which is the valid way to mark a decorative image and is not reported as an issue.
  • error is null on success. On failure it holds the reason (for example HTTP 404 for ..., Request timed out after 20000 ms, Not an HTML page (content-type: application/pdf) or Refused target: ...) and all analysis fields are null.

Rows are written as each page finishes, so their order follows completion, not input. The OUTPUT record summarizes the run:

{
"total": 137,
"audited": 134,
"failed": 3,
"skipped": 0,
"chargeLimitReached": false,
"source": "sitemap",
"sitemapUrl": "https://www.example-shop.com/sitemap.xml",
"checks": ["title", "description", "canonical", "robots", "og", "twitter", "h1", "hreflang", "imagesAlt"],
"issues": { "total": 361, "error": 4, "warning": 120, "info": 237 },
"byCode": [
{ "code": "description-too-short", "check": "description", "severity": "info", "pages": 71 },
{ "code": "title-too-long", "check": "title", "severity": "warning", "pages": 38 },
{ "code": "title-missing", "check": "title", "severity": "error", "pages": 4 }
],
"pagesWithErrors": 4,
"pagesWithoutIssues": 12,
"averageTitleLength": 58.3,
"averageDescriptionLength": 112.9,
"failedUrls": [{ "url": "https://www.example-shop.com/old-page", "error": "HTTP 404 for https://www.example-shop.com/old-page" }],
"skippedUrls": [],
"note": "Deterministic static checks of the HTML returned by each server (no JavaScript rendering). Length thresholds are common SEO guidelines, not search-engine limits. JSON-LD and detailed Open Graph / X Card validation are not covered; see bulk-rich-results-audit."
}

byCode[].pages counts pages, not issues. In the summary, audited is the number of pages fetched and parsed successfully. There is no overall score: the counts are the result.

Issue codes

Severities: error means a basic element is absent or broken; warning means it is likely to cause a problem; info is worth a look but can be intentional.

CheckCodeSeverityMeaning
titletitle-missingerrorNo <title> element (a <title> inside inline SVG is ignored).
titletitle-emptywarningThe <title> is empty.
titletitle-too-longwarningLonger than titleMaxLength (default 60 characters).
titletitle-too-shortinfoShorter than titleMinLength (default 50 characters).
titletitle-multipleinfoMore than one <title>.
descriptiondescription-missingwarningNo non-empty <meta name="description">.
descriptiondescription-too-long / description-too-shortinfoOutside descriptionMinLength-descriptionMaxLength (default 50-160 characters).
descriptiondescription-multipleinfoMore than one meta description.
canonicalcanonical-missinginfoNo <link rel="canonical">.
canonicalcanonical-invaliderrorThe canonical is not a valid http(s) URL.
canonicalcanonical-relativeinfoThe canonical is relative; an absolute URL is recommended.
canonicalcanonical-mismatchwarningThe canonical differs from the page's final URL. Often intentional for parameterized or duplicate URLs.
canonicalcanonical-conflictingwarningSeveral canonicals with different targets.
robotsrobots-noindexwarningnoindex or none in the robots/googlebot meta tag or the X-Robots-Tag header.
robotsrobots-nofollowinfonofollow or none in either place.
ogog-missingwarningNo og:* tags at all.
ogog-title-missing / og-image-missingwarningog:title or og:image absent or empty.
ogog-description-missinginfoog:description absent or empty.
twittertwitter-card-missinginfoNo twitter:card tag.
h1h1-missing / h1-emptywarningNo <h1>, or only empty ones.
h1h1-multipleinfoMore than one non-empty <h1>. Valid HTML and not a Google rule; some style guides prefer one.
hreflanghreflang-invalid-codewarningThe value is not a language code (optionally with script or region) or x-default.
hreflanghreflang-relative-urlwarningAn alternate URL is relative; they must be fully qualified.
hreflanghreflang-conflictingwarningThe same hreflang value points to more than one URL.
hreflanghreflang-missing-selfwarningThe page declares alternates but none points to itself.
imagesAltimage-alt-missingwarningOne or more images have no alt attribute.
pageresponse-truncatedinfoThe HTML was larger than 2 MB; tags after the cut were not seen.

Where the thresholds come from

Google states that there is no length limit for titles or meta descriptions and that they are truncated "as needed, typically to fit the device width" (title links, snippets). The length checks therefore use widely published SEO guidelines, are reported at modest severities, and can be changed with the input fields:

  • Title, 50-60 characters. Moz, title tag guide: "Titles between 51-60 characters tend to result in the fewest rewrites by Google" and, in the FAQ, "If you keep your titles under 60 characters, our research suggests that you can expect about 90% of your titles to display correctly in the SERPs" (Moz: title tag). Ahrefs: "it's best practice to keep title tags between 50 and 60 characters to reduce the risk of truncation" (Ahrefs: title tag SEO). Longer than the maximum is a warning. The 50-character minimum is the lower end of that range; below it is only info.
  • Meta description, 50-160 characters. Moz's meta description guide is not consistent with itself: the body says "The ideal length of a meta description is 120-160 characters" and "Descriptions longer than 160 characters risk being cut off in search results", while its FAQ says "Meta descriptions should be between 50-160 characters or around 680 pixels" (Moz: meta description). The 160-character maximum is supported by both statements. The 50-character minimum comes from the FAQ line only and is our own choice of the lenient end; if you prefer Moz's "ideal" range, set descriptionMinLength to 120. Both directions are reported as info.
  • Robots directives. noindex and none are interpreted as defined in Google's robots meta tag specification.
  • Hreflang. Code format, fully-qualified URLs and "each language version must list itself as well as all other language versions" follow Google's localized versions guide. The check does not verify that the other pages link back.
  • Image alt text. WCAG technique H37: "When using the img element, specify a short text alternative with the alt attribute" (H37); an <img> without alt is flagged. Separately, the W3C decorative images tutorial says "a null (empty) alt text should be provided ( alt="" ) so that they can be ignored by assistive technologies", which is why alt="" is not flagged (W3C: decorative images).
  • Open Graph. og:title, og:type, og:image and og:url are the required properties in the Open Graph protocol; this actor flags only the title and image, and og:description at info.

Third-party guideline pages (Moz, Ahrefs) can change; the defaults are editable for that reason. The quotes above were read on those pages on 2026-10-21.

Safety

  • Only the URLs you supply, or the <loc> entries of the sitemap you supply, are fetched. Links on pages are never followed. The robots.txt of the requested sites is not read; see ./TERMS.md.
  • Targets are checked before every request and every redirect: only http and https; no embedded credentials; no localhost, .local, .internal or single-label hostnames; no private, loopback, link-local, carrier-grade NAT, documentation, multicast or otherwise reserved IPv4/IPv6 addresses, including hostnames that resolve to one. The address is checked again when the connection is opened.
  • Limits: 5 redirects, 2 MB per page and per sitemap file (after decompression), 20 child sitemaps and 10 MB of sitemap data per run, 500 URLs, the configured timeout, one retry for network errors, timeouts, 408, 429 and 5xx.
  • Load on one host: with 500 URLs from the same site and the default concurrency of 10, the actor can send up to 10 simultaneous requests to that host and adds no delay between requests. Lower maxConcurrency for small or fragile sites.
  • Requests identify as Mozilla/5.0 (compatible; CuanticDataBot/1.0). No proxy is used, no cookies are sent and no login is attempted.

Limitations

  • No JavaScript. Only the HTML the server returns is checked. Tags injected by client-side scripts or tag managers are missed and may be reported as missing, and images added by scripts are not counted.
  • Guidelines, not rules. Title and description length checks are conventions (see above); search engines may show different text than your tags.
  • Presence only for social tags. Open Graph and X Card values are extracted and basic gaps are flagged. For URL syntax, conflicting duplicates, card types and fallback logic use Bulk Rich Results Audit.
  • No JSON-LD. Structured data is not analyzed here.
  • Hreflang is one-sided. Return links on the alternate pages are not fetched or verified.
  • Dynamic or personalized responses. Pages that serve different HTML by user agent, location or cookies may differ from what visitors or crawlers see. Pages behind a login, bot protection or a consent wall usually return that wall instead.
  • Max 500 URLs per run. Extra URLs are dropped with a warning. Split larger sites across several runs.
  • Sitemaps. Plain XML only (no .xml.gz files); a sitemap index is followed one level deep.

Pricing

Pay-per-event: one page-checked event per page that was fetched and parsed as HTML. Failed URLs (network errors, HTTP errors, non-HTML responses, refused targets) are not charged. See the Pricing tab for the current price. Apify platform usage is billed separately according to your plan.

If the maximum charge you set for a run is reached, the actor finishes the pages in progress, starts no new ones, and lists the URLs it did not start in OUTPUT.skippedUrls.

Development

npm install
npm test # unit tests (node:test, mocked network, no internet needed)
npm run smoke # optional: one real request to https://example.com/ (or SMOKE_URL); prints SKIPPED without egress

Support

Cuantic Data - cuanticwindows@gmail.com

Terms of use: see ./TERMS.md.