Page Metadata & SEO Extractor
Pricing
from $4.00 / 1,000 page checkeds
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
Maintained by CommunityActor 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-Tagheader), Open Graph and X/Twitter Card (presence of the basics), H1, hreflang and images without alt. Choose which checks to run with thechecksinput. - Reports issues as
{check, severity, code, message}(error,warningorinfo) 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
errormessage, 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,@typevalues 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 asog-missing,og-title-missing,og-image-missingoverlap 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
OUTPUTtotals: a jump incanonical-mismatchorrobots-noindexshows 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
| Field | Type | Default | Description |
|---|---|---|---|
urls | array of strings | - | Pages to check (http/https). Deduplicated (order kept), max 500. |
sitemapUrl | string | - | XML sitemap or sitemap index. Every <loc> page URL is checked, max 500. |
checks | array of strings | all | Any of title, description, canonical, robots, og, twitter, h1, hreflang, imagesAlt. Empty means all. Fields of unselected checks are null. |
titleMinLength / titleMaxLength | integer | 50 / 60 | Characters. Shorter gives an info issue, longer a warning. |
descriptionMinLength / descriptionMaxLength | integer | 50 / 160 | Characters. Outside the range gives an info issue. |
sameOriginOnly | boolean | true | For sitemaps: keep only child sitemaps and page URLs on the same origin as the sitemap URL. |
maxConcurrency | integer | 10 | Pages fetched in parallel, 1 to 10. |
timeoutMs | integer | 20000 | Timeout 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}
issuesholds every issue on the page, errors first.issueCountstotals them by severity.- A field is
nullwhen its check was not selected, when the page does not have it (for example no<title>), or when the page could not be fetched. CheckchecksRunto tell the cases apart. canonicalis resolved to an absolute URL (against<base href>if present).canonicalMatchesUrlcompares it to the final URL after redirects, ignoring the fragment and a trailing slash; query strings count.images.totalcounts every<img>;missingAltcounts images with noaltattribute (images markedrole="presentation",role="none"oraria-hidden="true"are ignored);emptyAltcountsalt="", which is the valid way to mark a decorative image and is not reported as an issue.errorisnullon success. On failure it holds the reason (for exampleHTTP 404 for ...,Request timed out after 20000 ms,Not an HTML page (content-type: application/pdf)orRefused target: ...) and all analysis fields arenull.
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.
| Check | Code | Severity | Meaning |
|---|---|---|---|
| title | title-missing | error | No <title> element (a <title> inside inline SVG is ignored). |
| title | title-empty | warning | The <title> is empty. |
| title | title-too-long | warning | Longer than titleMaxLength (default 60 characters). |
| title | title-too-short | info | Shorter than titleMinLength (default 50 characters). |
| title | title-multiple | info | More than one <title>. |
| description | description-missing | warning | No non-empty <meta name="description">. |
| description | description-too-long / description-too-short | info | Outside descriptionMinLength-descriptionMaxLength (default 50-160 characters). |
| description | description-multiple | info | More than one meta description. |
| canonical | canonical-missing | info | No <link rel="canonical">. |
| canonical | canonical-invalid | error | The canonical is not a valid http(s) URL. |
| canonical | canonical-relative | info | The canonical is relative; an absolute URL is recommended. |
| canonical | canonical-mismatch | warning | The canonical differs from the page's final URL. Often intentional for parameterized or duplicate URLs. |
| canonical | canonical-conflicting | warning | Several canonicals with different targets. |
| robots | robots-noindex | warning | noindex or none in the robots/googlebot meta tag or the X-Robots-Tag header. |
| robots | robots-nofollow | info | nofollow or none in either place. |
| og | og-missing | warning | No og:* tags at all. |
| og | og-title-missing / og-image-missing | warning | og:title or og:image absent or empty. |
| og | og-description-missing | info | og:description absent or empty. |
twitter-card-missing | info | No twitter:card tag. | |
| h1 | h1-missing / h1-empty | warning | No <h1>, or only empty ones. |
| h1 | h1-multiple | info | More than one non-empty <h1>. Valid HTML and not a Google rule; some style guides prefer one. |
| hreflang | hreflang-invalid-code | warning | The value is not a language code (optionally with script or region) or x-default. |
| hreflang | hreflang-relative-url | warning | An alternate URL is relative; they must be fully qualified. |
| hreflang | hreflang-conflicting | warning | The same hreflang value points to more than one URL. |
| hreflang | hreflang-missing-self | warning | The page declares alternates but none points to itself. |
| imagesAlt | image-alt-missing | warning | One or more images have no alt attribute. |
| page | response-truncated | info | The 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 onlyinfo. - 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
descriptionMinLengthto 120. Both directions are reported asinfo. - Robots directives.
noindexandnoneare 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>withoutaltis 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 whyalt=""is not flagged (W3C: decorative images). - Open Graph.
og:title,og:type,og:imageandog:urlare the required properties in the Open Graph protocol; this actor flags only the title and image, andog:descriptionatinfo.
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
httpandhttps; no embedded credentials; nolocalhost,.local,.internalor 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
maxConcurrencyfor 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.gzfiles); 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 installnpm 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.