Schema.org for LLMs Audit
Pricing
from $0.64 / 1,000 llm fact-readiness url audits
Schema.org for LLMs Audit
Audit public pages for AI-readable Organization, Product, and Article facts. Get a deterministic GEO readiness score, missing high-impact fields, cross-surface contradictions, evidence paths, and prioritized fixes without an LLM or browser.
Schema.org for LLMs Audit — GEO entity and product fact checker
Find the machine-readable facts that AI answer engines can verify about your brand, products, and articles — before a competitor becomes the cleaner answer. Submit 1–50 public URLs and receive one evidence-backed readiness audit per successfully fetched page: grade, score, entity inventory, high-impact gaps, contradictions, and prioritized fixes. No LLM key, browser, proxy, or search account is required.

The Actor reads only public static HTML that the target host permits through robots.txt. It compares
JSON-LD, Schema.org Microdata and RDFa, OpenGraph, canonical metadata, and the brand name you expect to
see. A successful audit is delivered to the default Dataset and charged as one result-found
event. A denied, unsafe, timed-out, oversized, or over-budget URL is written as a bounded free diagnostic
to the run's key-value store instead of becoming a paid pseudo-result.
Try the published example, Audit Apify's homepage for AI-readable brand facts, from the Examples tab. It uses a real public Organization page, requires no secret input, and demonstrates a Grade A result.
What you get
Each successful URL produces a decision record rather than a loose scrape. The record answers five practical questions:
- What machine-readable entity is present? The Actor recognizes Organization and LocalBusiness subtypes, Product variants, Article variants, JSON-LD graphs, Microdata scopes, and RDFa scopes.
- Can an answer engine identify the brand? It checks stable entity IDs, names, URLs, logo URLs,
authoritative
sameAsdomains, publisher references, and an optional buyer-supplied brand. - Can an answer engine understand the offer or content? Product records expose brand, SKU/GTIN, offer price, currency, and availability; Article records expose headline, publication dates, publisher, author presence, and image.
- Do the surfaces agree? Canonical URL, OpenGraph URL/title/site name/product price, and structured entities are reconciled. Conflicts are named, severity-ranked, and tied to both evidence surfaces.
- What should the marketer or developer fix next? Missing high-impact facts and contradictions are
translated into bounded actions such as adding a stable Organization
@id, completing Product Offer fields, or aligning OpenGraph and canonical metadata.
The Dataset includes the original normalized URL, final URL after guarded redirects, HTTP status, document title and language, detected document types, normalized target entities, OpenGraph summary, fact inventory, 0–100 readiness score, A–F grade, missing-fact codes, contradictions, fixes, quality flags, evidence paths, expected-brand match, static-HTML disclosure, and one run-wide observation time.
This is deliberately a deterministic audit, not a synthetic AI opinion. The same HTML and settings produce the same findings. The Actor does not ask a model whether a page “looks good,” invent a citation probability, pretend that structured data guarantees ranking, or claim access to proprietary answer engine indexes. It measures whether important public facts are present, attributable, internally consistent, and straightforward for automated systems to parse.
The run also provides three operational records:
OUTPUT— requested, normalized, attempted, audited, delivered, billed, quarantined, and budget-withheld counts plus pricing seen by the running build.COVERAGE— grade distribution, document-type distribution, and diagnostic reason counts.QUARANTINE— short free diagnostics for URLs that did not become Dataset rows. Response bodies, private addresses, credentials, and Person entity details are never copied there.
Useful platform behavior comes with the product: save the input as a Task, schedule recurring audits, trigger runs through the API, export the Dataset as JSON/CSV/Excel, connect webhooks, or expose the Actor to an AI client through Apify's hosted MCP server. The output schema makes the most useful fields visible in the Store table instead of forcing a buyer to inspect raw logs.
Who uses it
GEO and AEO marketers use the grade and gap list to turn a vague “improve AI visibility” request into
a page-level backlog. A homepage may need a stable Organization identity and authoritative
sameAs links; a product page may need complete Offer facts; a thought-leadership article may
need publisher and date provenance.
Brand and communications teams use expectedBrandName and brandAliases to
detect pages whose structured entity name or og:site_name no longer matches the accepted
brand. This is especially useful after a rename, merger, localization project, or multi-brand site
migration. Matching is token-aware and does not treat a short brand such as “AI” as an arbitrary
substring.
Technical SEO teams use the normalized fact inventory and evidence paths to reproduce findings without hunting through a large HTML document. The result distinguishes valid and invalid JSON-LD blocks, JSON-LD versus Microdata/RDFa entities, canonical presence, OpenGraph completeness, and the number of consistency checks actually possible on that page.
E-commerce marketers use Product findings to identify pages where a crawler sees a product name but
cannot reliably connect it to a brand, offer price, currency, availability, SKU, or GTIN. Prices are
normalized before comparison, so formatting such as 1,299.00 and 1299 does not
create a false mismatch.
Content operations teams use Article findings to check headline, publication or modification date, publisher, author presence, and image. The Actor reports that a Person reference exists but deliberately does not output a person's name, profile, or other details.
Agencies use batch inputs to audit the same small, high-value page set for every client and export a standardized evidence file. The output is suited to a client brief because every score is accompanied by the facts, gaps, contradictions, and source paths that produced it.
Product teams and automation builders use the stable codes for routing. A workflow can open a ticket only for high-severity contradictions, notify a marketer when the expected brand does not match, or compare score distributions over scheduled runs without parsing prose.
This Actor is not a general crawler, a JavaScript renderer, a search-rank tracker, an AI citation monitor, a legal compliance scanner, or a substitute for Schema.org validation. It complements those tools by answering a narrower commercial question: does this public page expose a coherent, useful set of brand, product, or article facts to automated readers right now?

How to run
Fastest Store run
- Open the Actor and choose Try for free.
- Add one to fifty public HTTP(S) URLs. Start with pages that represent the brand: homepage, flagship product pages, evergreen guides, and important editorial pages.
- Optionally enter the expected public brand name and accepted aliases.
- Keep maximum concurrency at 4 unless the target site explicitly supports a higher request rate.
- Set a maximum run charge that covers the start event and the number of results you want.
- Run the Actor, then open Output → Dataset for audits and Output → Key-value store for the run receipt, coverage summary, or free diagnostics.
Minimal input:
{"urls": ["https://apify.com/"],"expectedBrandName": "Apify","maxConcurrency": 2}
Typical three-page brand audit:
{"urls": ["https://example.org/","https://example.org/products/flagship","https://example.org/blog/research-report"],"expectedBrandName": "Example","brandAliases": ["Example Inc.","Example Labs"],"maxConcurrency": 3}
The Actor removes URL fragments and exact normalized duplicates before fetching. A URL without a scheme is normalized to HTTPS. Credentials embedded in a URL are rejected. Only ordinary public HTTP(S) destinations are eligible; private, loopback, link-local, metadata, documentation, benchmark, transition, and other special-use address ranges fail closed.
Choose pages intentionally
A 50-page random crawl is rarely the best first audit. Start with one representative page for each decision type:
| Business question | Recommended page | Primary facts |
|---|---|---|
| Can an AI system identify us? | Homepage or About page | Organization name, URL, logo, stable ID, sameAs |
| Can it describe the offer? | Product detail page | Product, brand, SKU/GTIN, Offer price/currency/availability |
| Can it attribute our content? | Article or report | headline, dates, publisher, author presence, image |
| Did a migration create conflicts? | Old and new canonical pages | canonical, OpenGraph URL, entity URL, brand name |
| Are localized pages coherent? | One page per locale | page language, identity, URL and metadata alignment |
For monitoring, save the input as an Apify Task and schedule it at a cadence appropriate to the site.
Daily runs make sense during a migration; weekly or monthly runs are usually enough for stable pages.
The Actor does not maintain history itself. Store Dataset exports externally or compare scheduled run
outputs by url, readinessScore, and stable finding codes.
Read the run in the right order
First inspect OUTPUT.counts. For an accepted run,
delivered equals billed on platform and equals the default Dataset item count.
Then inspect COVERAGE to see whether the batch is mostly strong, weak, or diagnostic. Finally
use each row's missingHighImpactFacts, contradictions, and
evidencePaths to assign work.
An empty Dataset is not automatically a failed run. It may mean every target was denied by robots.txt,
unsafe, unavailable, oversized, non-HTML, or withheld by the buyer's charge limit. The reason will be in
QUARANTINE and the aggregate count will be in OUTPUT.
Pricing
The Actor uses Apify PAY_PER_EVENT pricing. There are only two billable nouns:
apify-actor-start— the platform start event for the run.result-found— one successfully fetched, audited, and atomically delivered URL result.
The default Dataset event is intentionally not priced. Errors and diagnostic records are not
result-found events. The running Actor reads its platform pricing and buyer charge budget
before starting network work; if pricing is unreadable or misconfigured, it fails closed.
| Tier | Start event | Per delivered result-found | Discount from FREE |
|---|---|---|---|
| FREE | $0.0050000 | $0.0007500 | 0% |
| BRONZE | $0.0047500 | $0.0007125 | 5% |
| SILVER | $0.0045000 | $0.0006750 | 10% |
| GOLD | $0.0042500 | $0.0006375 | 15% |
| PLATINUM | $0.0041000 | $0.0006150 | 18% |
| DIAMOND | $0.0040000 | $0.0006000 | 20% |
Pricing calculator
Use:
estimated Actor charge = start price + delivered audit count × result price
Examples, before any taxes or account-specific platform terms:
| Delivered audits | FREE | BRONZE | SILVER | GOLD | PLATINUM | DIAMOND |
|---|---|---|---|---|---|---|
| 1 | $0.005750 | $0.005463 | $0.005175 | $0.004888 | $0.004715 | $0.004600 |
| 10 | $0.012500 | $0.011875 | $0.011250 | $0.010625 | $0.010250 | $0.010000 |
| 20 | $0.020000 | $0.019000 | $0.018000 | $0.017000 | $0.016400 | $0.016000 |
| 50 | $0.042500 | $0.040375 | $0.038250 | $0.036125 | $0.034850 | $0.034000 |
The input limit is 50 URLs, but fewer than 50 can become billable results. Invalid duplicates, robots denials, failed DNS, unsafe destinations, non-HTML responses, timeouts, oversized HTML, and pages withheld by the charge ceiling stay free diagnostics. A successfully fetched page with a low score is still a real delivered audit and is billable: “this page has no useful machine-readable facts” is often the most valuable result in the batch.
At the beginning of a run, the Actor estimates how many result events fit after the amount already
charged. URLs beyond that affordable count are not fetched and receive a
buyer_budget_not_attempted diagnostic. Immediately before each successful delivery, it
re-checks the budget. This avoids doing paid work that cannot be delivered and avoids silently pushing
unbilled results.
The accepted 20-URL canary used BRONZE pricing and a $0.05 maximum charge. It delivered and billed 20
audits, with a result-found price of $0.0007125 and no Dataset-item price. The source and
network cost measured after the run left the conservative result-only revenue/COGS ratio above 17×.
Input contract
The input is a strict JSON object. Unknown fields are rejected instead of being silently ignored.
urls — required
- Type: array of strings.
- Minimum: 1 item.
- Maximum: 50 items.
- Per-item limit: 2,048 characters.
- Accepted schemes: HTTP and HTTPS only.
- Behavior: surrounding whitespace is removed; a missing scheme becomes HTTPS; fragments are removed; exact normalized duplicates are quarantined without a fetch.
- Rejected during input normalization: embedded username/password, malformed URL, empty string, and
control characters. A syntactically valid URL that resolves to a non-public destination is instead
stopped before target-content fetch and recorded as a free
unsafe_private_targetdiagnostic.
expectedBrandName — optional
- Type: string, 1–100 characters.
- Purpose: deterministic comparison against Organization names, Product brands, publishers, and
og:site_name. - Privacy: used inside the run only; it is never sent to an AI service because the Actor calls no AI service.
- Interpretation: absence returns
expectedBrandMatch: null; a supplied brand returns true or false and may addEXPECTED_BRAND_NOT_MATCHED.
brandAliases — optional
- Type: unique array of strings.
- Maximum: 20 aliases, each 1–100 characters.
- Requires:
expectedBrandName. - Purpose: accept public variants such as a legal company suffix, former brand, abbreviation, or localized spelling without weakening matching to arbitrary substrings.
maxConcurrency — optional
- Type: integer from 1 through 10.
- Default and prefill: 4.
- Purpose: number of page inspections that may proceed in parallel.
- Recommendation: keep 2–4 for a single host. Higher concurrency does not bypass target policy and may increase throttling risk.
The Actor deliberately has no proxy, browser, cookie, login, user-agent override, JavaScript-rendering, LLM key, model, prompt, recursive crawl, or “ignore robots” setting. Those omissions are product boundaries, not hidden advanced options.
Real happy, partial and failure output
The following examples separate observed live behavior from contract-shaped boundary examples. They are not invented performance claims.
Happy output — public Organization page
The published Example Task audits https://apify.com/ with expected brand “Apify.” The page
currently exposes a valid Organization entity, canonical URL, complete OpenGraph core, stable
@id, logo, and multiple authoritative sameAs domains. Dynamic web content can
change, so timestamps and peripheral document types will vary. The JSON below is explicitly abridged:
its three documentTypes and three sameAsDomains are representative subsets of
the larger arrays returned by the observed page.
{"schemaVersion": "1.0","url": "https://apify.com/","finalUrl": "https://apify.com/","httpStatus": 200,"title": "Apify: The largest marketplace of trusted tools for AI","canonicalUrl": "https://apify.com/","pageLanguage": "en","documentTypes": ["Organization","SoftwareApplication","WebSite"],"entities": [{"entityType": "Organization","schemaTypes": ["Organization"],"entityId": "https://apify.com/#organization","name": "Apify","url": "https://apify.com/","logoUrl": "https://apify.com/img/apify-logo/apify-symbol-200x200.svg","sameAsDomains": ["github.com", "linkedin.com", "youtube.com"],"sourcePath": "/jsonLd/0"}],"readinessScore": 100,"grade": "A","missingHighImpactFacts": [],"contradictions": [],"fixes": [],"expectedBrandMatch": true,"staticHtmlOnly": true}
Weak but successful output — observed paid canary
The accepted private canary fetched 20 distinct public example.com query URLs from the
exact deployed runtime. Every fetch succeeded and therefore produced a real paid audit. All 20 were
Grade F because that intentionally minimal page had no JSON-LD target entity, canonical URL, or
OpenGraph core. The JSON below is an abridged view of that real row: it omits
auditId, checkedAt, openGraph,
evidencePaths, some inventory fields, and all but one observed fix:
{"schemaVersion": "1.0","url": "https://example.com/?schema-canary=01","finalUrl": "https://example.com/?schema-canary=01","httpStatus": 200,"title": "Example Domain","pageLanguage": "en","documentTypes": [],"entities": [],"factInventory": {"jsonLdBlocks": 0,"validJsonLdBlocks": 0,"invalidJsonLdBlocks": 0,"targetEntityCount": 0,"organizationCount": 0,"productCount": 0,"articleCount": 0,"hasCanonical": false,"hasOpenGraphCore": false,"consistencyChecks": 0},"readinessScore": 0,"grade": "F","missingHighImpactFacts": ["NO_VALID_JSON_LD","NO_TARGET_ENTITY","NO_ORGANIZATION_IDENTITY","NO_CANONICAL_URL","OPEN_GRAPH_CORE_INCOMPLETE","EXPECTED_BRAND_NOT_MATCHED"],"contradictions": [],"qualityFlags": ["no_target_machine_readable_entity","open_graph_absent","canonical_absent"],"fixes": [{"code": "EXPECTED_BRAND_NOT_MATCHED","action": "Align Organization/Product brand names and og:site_name with an accepted buyer-supplied brand name."}],"expectedBrandMatch": false,"staticHtmlOnly": true}
The live run receipt proved the delivery and pricing invariant:
{"status": "complete","counts": {"requested": 20,"normalizedUnique": 20,"affordable": 20,"attempted": 20,"audited": 20,"delivered": 20,"billed": 20,"quarantined": 0,"budgetWithheld": 0},"billing": {"resultEvent": "result-found","resultPriceUsd": 0.0007125,"datasetPriceUsd": 0,"maxTotalChargeUsd": 0.05,"spentBeforeUsd": 0.00475}}
Partial content — supported and explicitly flagged
A page may be fetched successfully and expose a Product while omitting important commerce facts. That is a billable audit, not a network failure. The precise values depend on the page, but the supported contract looks like this:
{"documentTypes": ["Product"],"entities": [{"entityType": "Product","name": "Example Widget","brand": "Example","offerPrice": null,"offerCurrency": null,"availability": null,"sourcePath": "/jsonLd/0"}],"readinessScore": 71,"grade": "B","missingHighImpactFacts": ["PRODUCT_COMMERCE_FACTS_INCOMPLETE"],"fixes": [{"code": "PRODUCT_COMMERCE_FACTS_INCOMPLETE","action": "Add Product brand plus Offer price, priceCurrency and availability."}],"staticHtmlOnly": true}
This exact missing-field path is covered by the deployed parser's Product fixtures. It is shown as a contract example, not presented as an observation from the public Apify homepage.
Free failure diagnostic
Failures do not enter the default Dataset. A private address, robots denial, timeout, oversized document,
unsupported content type, or exhausted budget produces a bounded record in QUARANTINE:
{"url": "http://127.0.0.1/admin","reason": "unsafe_private_target","httpStatus": null}
or:
{"url": "https://example.org/private-page","reason": "robots_disallowed"}
No fetched response body is copied into a diagnostic. A completed run with only diagnostics can have
zero Dataset items, zero result-found events, and a non-empty QUARANTINE.
Field dictionary
Top-level audit identity
| Field | Type | Meaning |
|---|---|---|
schemaVersion | string | Output contract version, currently 1.0 |
auditId | string | SHA-256 of requested URL, final URL, and run-wide observation time |
url | string | Normalized buyer-supplied URL |
finalUrl | string | Final URL after up to five guarded redirects |
httpStatus | integer | Successful HTML response status |
title | string or null | Normalized HTML title |
canonicalUrl | string or null | First safe absolute canonical URL |
pageLanguage | string or null | Normalized HTML lang value |
checkedAt | ISO timestamp | One shared observation time for every row in a run |
staticHtmlOnly | boolean | Always true; reminds downstream users of the rendering boundary |
Entity and document fields
| Field | Type | Meaning |
|---|---|---|
documentTypes | string array | Up to 40 normalized Schema.org types seen in supported markup |
entities | object array | Up to 20 normalized Organization, Product, or Article entities |
entityType | enum | Organization, Product, or Article |
schemaTypes | string array | Original normalized type family for the entity |
entityId | string or null | Safe normalized Schema.org @id or embedded identifier |
name | string or null | Organization/Product name or Article headline |
url | string or null | Entity URL resolved against the page |
logoUrl | string or null | Organization logo URL |
imageUrl | string or null | Product/Article image URL |
sameAsDomains | string array | Hostnames from Organization sameAs; URLs are reduced to domains |
brand | string or null | Product brand name |
sku / gtin | string or null | Product identifiers when published |
offerPrice | string or null | Product offer price as normalized source text |
offerCurrency | string or null | Product offer currency |
availability | string or null | Terminal Schema.org availability term |
headline | string or null | Article headline |
datePublished / dateModified | string or null | Source date text |
publisher | string or null | Article publisher reference |
authorPresent | boolean or null | Whether an author reference exists; Person details are excluded |
sourcePath | string | Bounded JSON-LD, Microdata, or RDFa path |
OpenGraph and inventory
openGraph contains title, type, URL, site name, description/image presence, and optional
product price/currency. factInventory records:
- total, valid, and invalid JSON-LD blocks;
- total target entities and counts by Organization, Product, and Article;
- ignored Person entities;
- target entities by JSON-LD, Microdata, and RDFa;
- normalized Microdata and RDFa type lists;
- canonical and OpenGraph-core presence;
- number of cross-surface consistency checks that could actually be made.
Decision fields
| Field | Type | Meaning |
|---|---|---|
readinessScore | integer 0–100 | Weighted fact coverage minus contradiction and brand-mismatch penalties |
grade | A–F | A ≥ 85, B ≥ 70, C ≥ 55, D ≥ 40, otherwise F |
missingHighImpactFacts | code array | Important absent or incomplete facts, maximum 20 |
contradictions | object array | Code, severity, and compared surfaces, maximum 20 |
fixes | object array | One actionable recommendation per gap or contradiction, maximum 20 |
qualityFlags | code array | Parsing/context flags such as invalid JSON-LD or absent canonical |
evidencePaths | object | Source paths supporting important normalized facts |
expectedBrandMatch | boolean or null | Match result when an expected brand was supplied |
An A is reserved for a result with no declared high-impact gap and no contradiction. Even if raw field coverage would otherwise qualify for A, any declared gap or contradiction caps the score at 84. This prevents a page with a critical missing fact from receiving a misleading top grade.
Run records
OUTPUT is the audit receipt. COVERAGE groups grades, document types, and
diagnostics. QUARANTINE holds free, bounded per-URL diagnostics. These are key-value-store
records, not Dataset rows and not separately priced result events.
Evidence and boundaries
The Actor collects evidence from five public surfaces:
application/ld+json, including arrays, nested objects, and@graphentries up to bounded recursion and value limits.- Schema.org Microdata scopes using
itemscope,itemtype,itemprop, and supported scalar attributes. - RDFa scopes using
typeof,property,about,resource, and supported scalar attributes. - OpenGraph metadata, including common product price and currency properties.
- HTML canonical, title, and language metadata.
Evidence paths point to normalized source locations such as /jsonLd/0/name or
/microdata/2. They make a finding reproducible, but they are not full JSONPath expressions
against a preserved source document. The Actor does not store the HTML body.
Network authorization is fail-closed. Before the first target request and before every redirect hop, the Actor:
- normalizes and validates the URL;
- loads the current origin's robots.txt;
- applies the most specific matching rule for its named user agent;
- resolves DNS and rejects the host if any returned address is special-use or non-global;
- pins the connection to one already verified address, preferring IPv4 when available;
- follows at most five redirects;
- caps robots.txt at 100 KB, HTML at 750 KB, and each request at 20 seconds.
This design narrows DNS rebinding and server-side request forgery risk. It does not claim to be a general-purpose security scanner, firewall, or legal authorization service.
The extraction boundary is initial static HTML only. Metadata inserted after page load by JavaScript is invisible. There is no headless browser and no attempt to execute scripts. This keeps cost and behavior predictable but means a page may score lower than a browser-based validator shows.
Person details are excluded even when nested inside an Article or Organization graph. The Actor may
return authorPresent: true and increment ignoredPersonEntities, but it does not
return a person's name, profile URL, image, email, job title, or social accounts.
The score is an operational prioritization aid. It is not:
- proof that an AI answer engine crawled, indexed, cited, ranked, or recommended the page;
- a Google rich-results eligibility decision;
- a full Schema.org syntax validator;
- a replacement for content quality, authority, accessibility, or conventional technical SEO review;
- a legal opinion about copyright, database rights, privacy, terms of service, or automated access.
Decision routing
The Actor is easiest to automate when routing on stable fields rather than prose.
| Condition | Suggested route | Why |
|---|---|---|
| Grade A and expected brand true | Monitor | Core machine-readable facts are present and coherent |
| Grade B/C with missing facts | Create structured-data ticket | Coverage is useful but an explicit high-impact gap remains |
| High-severity contradiction | Escalate before publishing campaign | Two public machine surfaces disagree |
| Expected brand false | Brand/governance review | Page identity does not match accepted names |
| No target entity | Technical SEO backlog | Automated readers lack a supported primary entity |
| Product commerce incomplete | E-commerce schema owner | Offer facts cannot be reliably used |
| Article provenance incomplete | Editorial platform owner | Attribution and freshness are weak |
| Diagnostic reason | Access/operations queue, no SEO ticket yet | The page was not audited successfully |
Example pseudo-routing:
if row.contradictions contains severity high:open urgent metadata-consistency ticketelse if row.expectedBrandMatch is false:notify brand governanceelse if row.missingHighImpactFacts is not empty:add fixes to technical SEO backlogelse:record healthy observation
Do not compare auditId across runs as a stable page identifier because its timestamp is
intentionally part of the hash. Join history on normalized url. Compare
readinessScore, grade, finding codes, and selected entity facts. Keep the
source checkedAt so a later page change is not mistaken for inconsistent parsing.
For a batch, route operational failures first by QUARANTINE.reason. A robots denial should
not create the same ticket as an expected-brand mismatch because no page audit occurred. A
buyer_budget_not_attempted record is solved by changing run budget or splitting the task,
not by changing the target site.
Commercial playbooks
GEO readiness baseline for a new client
Choose the homepage, About page, two revenue-driving Product pages, and two authoritative Articles. Run with the client's accepted brand name and aliases. Deliver:
- score and grade by page;
- one consolidated list of high-impact gap codes;
- contradictions with both compared surfaces;
- top three fixes per page;
- screenshots or source snippets collected manually only where the client needs implementation detail.
The Actor supplies the evidence map and prioritization. The agency supplies context, ownership, and implementation. This is a credible paid audit because the deliverable is reproducible and bounded.
Rebrand or domain-migration quality control
Audit the old homepage, new homepage, key redirected Product URLs, and major Articles. Compare canonical, entity URL, OpenGraph URL, entity name, and expected-brand result. High-severity mismatches can reveal a page whose visible content migrated while its JSON-LD still names the old domain or brand.
Run daily during launch week, then weekly until results stabilize. Keep URLs in the same saved Task so Dataset diffs remain simple.
E-commerce structured-offer backlog
Submit the highest-traffic or highest-margin Product pages, not the entire catalog. Group findings:
- Product entity absent;
- brand absent or inconsistent;
- Offer price/currency/availability incomplete;
- identifier absent;
- OpenGraph product price conflicts with structured Offer.
Route the first three groups to the template/schema owner because one template fix may improve thousands of pages. Use the result count to estimate rollout impact; do not imply that each audit equals an AI citation.
Editorial authority and provenance review
Audit evergreen guides, original research, and executive thought leadership. Filter Article rows for missing publisher, author presence, publication dates, or image. Verify that the Organization identity on the site is also complete. This creates a compact provenance checklist for editorial operations.
Multi-brand governance
Create one Task per brand with its own expected name and aliases. Schedule the same page families. Send only false brand matches and high-severity contradictions to the shared governance queue. This avoids asking a reviewer to read every healthy result.
Lead magnet or freemium audit
An agency can offer a five-page baseline as a low-cost diagnostic, then sell implementation and monitoring. The Actor's price is measured per delivered page; at FREE tier, a five-result run is approximately $0.00875 including one start event. Present the report honestly as structured-fact readiness, not guaranteed AI ranking.
Integration recipes
REST API with cURL
Start a run and let the API wait up to its supported 60-second maximum:
curl -X POST \"https://api.apify.com/v2/acts/zinin~schema-org-for-llms-audit/runs?token=<APIFY_TOKEN>&waitForFinish=60&maxTotalChargeUsd=0.02" \-H "Content-Type: application/json" \-d '{"urls": ["https://apify.com/"],"expectedBrandName": "Apify","maxConcurrency": 2}'
If the returned run status is still transitional, read data.id from that response and poll
GET https://api.apify.com/v2/actor-runs/<RUN_ID> until it is terminal. A terminal
SUCCEEDED response carries defaultDatasetId; use that ID to retrieve rows:
$curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json&token=<APIFY_TOKEN>"
Keep tokens in a secret manager or request header in production; do not commit them to source control.
JavaScript client
import { ApifyClient } from "apify-client";const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor("zinin/schema-org-for-llms-audit").call({urls: ["https://apify.com/"],expectedBrandName: "Apify",maxConcurrency: 2}, {maxTotalChargeUsd: 0.02});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.map(({ url, grade, readinessScore, fixes }) => ({url, grade, readinessScore, fixes})));
Python client
from apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("zinin/schema-org-for-llms-audit").call(run_input={"urls": ["https://apify.com/"],"expectedBrandName": "Apify","maxConcurrency": 2,},max_total_charge_usd=0.02,)rows = client.dataset(run["defaultDatasetId"]).list_items().itemsfor row in rows:print(row["url"], row["grade"], row["readinessScore"])
Apify MCP server
Connect an MCP-compatible client to https://mcp.apify.com with OAuth, or provide an Apify
token as a Bearer header. Enable the Actor directly:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com?tools=zinin/schema-org-for-llms-audit"}}}
The client exposes the selected Actor as a tool and includes
get-actor-output for structured results. Alternatively enable the general
call-actor tool and pass Actor ID zinin/schema-org-for-llms-audit. Running
Actors and reading their storage requires authenticated MCP access. The Actor uses
LIMITED_PERMISSIONS and is compatible with Apify's MCP execution surface.
Saved Task and schedule
Save the desired URL set as a Task, run it once manually, then attach an Apify Schedule. Use a webhook
for terminal run status and read the default Dataset plus OUTPUT after success. If a
downstream system needs only changes, compare the newest rows to the previous run by URL and finding
codes before sending notifications.
Make, Zapier, Sheets, BI, or warehouse
Use the run-finished trigger, then export the default Dataset. Flatten:
url,grade,readinessScore, andexpectedBrandMatchas dimensions;- lengths of gaps and contradictions as triage metrics;
- finding codes as exploded child rows when your warehouse supports them;
checkedAtas observation time, not ingestion time.
Keep entities, fixes, and evidencePaths as JSON if the destination
supports semi-structured columns. Do not drop staticHtmlOnly; it is an important
interpretation boundary.
Operating guide
Before the first production run
- Confirm each URL is public and intended for automated access.
- Choose a charge ceiling using the pricing calculator.
- Use an expected brand only when the accepted public spelling is known.
- Start with low concurrency and a small representative sample.
- Decide where historical outputs will live; the Actor does not create a long-term trend database.
After every run
- Confirm terminal status is
SUCCEEDED. - Compare Dataset item count with
OUTPUT.counts.deliveredand, on platform,OUTPUT.counts.billed. - Review
QUARANTINEbefore concluding that missing Dataset rows mean missing metadata. - Sort Dataset rows by contradiction severity, expected-brand mismatch, missing-fact count, then score.
- Preserve run ID and checked time with any exported report.
Troubleshooting
Dataset is empty, but the run succeeded. Open QUARANTINE. The most common reasons are
robots denial, HTML over 750 KB, non-HTML response, timeout, or charge-budget withholding.
The page looks rich in a browser but scores poorly. Its structured metadata may be inserted by client JavaScript. This Actor reads only initial static HTML. Use a browser-based validator to confirm the difference, then decide whether server-rendering critical facts is appropriate.
The expected brand is false even though the brand appears in body copy. Body copy is not a supported
identity surface. Publish the accepted name in Organization/Product/publisher metadata or
og:site_name, or add a legitimate alias to input.
A Product price conflict looks surprising. Compare contradictions and
evidencePaths. The Actor normalizes numeric formatting but does not perform currency
conversion, tax interpretation, regional price selection, or offer aggregation beyond its bounded
primary-value selection.
A target is quarantined as unsafe. The hostname resolved to at least one non-global address or was a special-use literal. This is not overrideable. Use the Actor only for public web destinations.
The run stopped near its charge ceiling. The platform may have charged the start event before result
delivery. Increase maxTotalChargeUsd or reduce/split the URL list. Withheld pages remain free.
Monitoring drift
For scheduled runs, alert on:
- new high-severity contradiction codes;
- transition from expected-brand true to false;
- disappearance of the target entity;
- grade decline of two or more levels;
- sudden
robots_disallowedorhtml_too_largediagnostics; - divergence among requested, delivered, billed, and Dataset counts.
Do not alert only on a one-point score change. Small field changes can alter weighted coverage without creating a commercially meaningful regression.
FAQ
Does structured data make my brand appear in ChatGPT, Gemini, Claude, or another answer engine?
No guarantee is possible. Structured facts can make a page easier for automated systems to interpret, but crawl access, indexing, authority, retrieval, model behavior, and answer context remain outside this Actor. The output measures public fact readiness, not citations or rank.
Does the Actor call an LLM?
No. It calls no OpenAI, Anthropic, Google, OpenRouter, Chinese-model, search, or embedding API. Scoring and recommendations are deterministic code.
Does it render JavaScript?
No. It audits initial static HTML. Client-injected JSON-LD is outside scope and explicitly disclosed by
staticHtmlOnly: true.
Why is a Grade F page billable?
Because the page was safely fetched, audited, and delivered, and “no usable target facts were present” is a valid decision result. Network failures, denials, unsafe targets, and budget withholding are free diagnostics instead.
Can it crawl an entire domain?
No. You provide exact URLs, up to 50 per run. There is no sitemap discovery, link traversal, or recursive crawl. This protects scope, cost, and target load.
Can I bypass robots.txt or use a proxy?
No. Robots checks are mandatory per origin and redirect hop. The Actor exposes no proxy or bypass setting.
What entity types are audited?
Organization and many LocalBusiness/organization subtypes, Product variants, and Article variants.
Other Schema.org types can appear in documentTypes but are not normalized as primary target
entities.
Are Person entities returned?
No. Their details are excluded. For an Article, the Actor reports only whether an author reference is present.
Does it validate every Schema.org rule?
No. It parses bounded facts needed for this decision product. Use the Schema.org validator and relevant search-engine rich-result tools for full vocabulary and platform-specific validation.
Can I compare two runs?
Yes. Join rows by normalized URL and compare score, grade, finding codes, contradictions, and selected
entity facts. Keep checkedAt. Do not use auditId as a time-invariant page key.
Why can the output differ tomorrow?
The target page, robots.txt, DNS, redirect chain, or static markup may change. The Actor reports live public evidence at run time and does not cache page bodies.
What if robots.txt is missing?
HTTP 404 or 410 for robots.txt is treated as no declared rule. Unreadable, denied, oversized, or HTML-shaped robots responses fail closed because policy cannot be established reliably.
What counts as a contradiction?
The five implemented contradiction codes are CANONICAL_OG_URL_MISMATCH (canonical URL
versus OpenGraph URL), TITLE_OG_TITLE_MISMATCH (HTML title versus OpenGraph title),
ORG_OG_SITE_NAME_MISMATCH (Organization name versus OpenGraph site name),
PRODUCT_PRICE_MISMATCH (normalized Product Offer price versus OpenGraph product price), and
PRODUCT_CURRENCY_MISMATCH (Offer currency versus OpenGraph product currency). Each returned
object names its code, severity, and compared surfaces. An expected-brand mismatch is a missing-fact code,
EXPECTED_BRAND_NOT_MATCHED, not a contradiction object.
Is my input sent anywhere else?
It is used inside the Apify run to fetch the public targets. There is no child Actor or external AI service. Normal Apify platform storage and networking still apply to the run.
Can I get CSV or Excel?
Yes. Use Apify Dataset export formats. Nested entities, gaps, contradictions, and evidence are most complete in JSON; flattened formats may serialize them as nested values.
How should I report a reproducible issue?
Provide the run ID, exact normalized URL, row checkedAt, relevant finding code, and whether
the discrepancy concerns static HTML or browser-rendered DOM. Never send private credentials.
Complete the AI visibility workflow
| Related Actor | Use it for |
|---|---|
| AI Crawler Access Checker | Verify robots.txt access for major AI crawler user agents |
| llms.txt Auditor | Audit an llms.txt file and crawler-policy alignment |
| AI Answer Change Alert | Detect changes in collected answer and citation datasets |
| AI Overview Citation Tracker | Track cited domains in Google AI Overview observations |
Use this Actor for page-level facts, the access Actors for crawler policy, and citation Actors for observed answer surfaces. None of them should be interpreted as a guaranteed ranking forecast.
Sources and rights
The Actor reads buyer-supplied public HTTP(S) pages and each target origin's public robots.txt. It does not search for URLs, log in, accept cookies, access a private API, bypass a paywall, solve a challenge, or reproduce a site-specific database.
The target page remains the source of every extracted fact. Output contains normalized facts and bounded evidence paths, not the HTML body, article text, image binaries, or Person profiles. Logo and image values are source URLs only; the Actor does not download or redistribute the assets.
Robots.txt is checked as an automated-access signal before each initial and redirected request. It is not a legal license or a complete statement of site terms. Buyers are responsible for confirming that their chosen targets and intended downstream use comply with applicable terms, contracts, copyright, database rights, privacy rules, and law. This page does not provide legal advice.
The scoring framework and fix codes are this Actor's deterministic analysis. Schema.org is a public vocabulary; OpenGraph and HTML metadata are public web conventions. The Actor does not claim affiliation with Schema.org, Google, OpenAI, Anthropic, Microsoft, Meta, or any target brand.
Operational behavior follows Apify's public platform contracts:
Target sites can change markup, robots policy, DNS, redirects, or availability without notice. A successful observation is evidence for its timestamp, not a warranty of future access or format.
Limits
- 1–50 exact URLs per run; no discovery or recursive crawl.
- Static HTML only; no JavaScript rendering.
- 750 KB maximum HTML and 100 KB maximum robots document.
- 20-second per-request timeout and five redirect hops.
- Maximum 20 normalized target entities, 40 document types, 20 gaps, 20 contradictions, and 20 fixes per row; bounded parsing prevents untrusted pages from creating unbounded output.
- Organization, Product, and Article families only as primary entities.
- No Person details.
- No review of visible body copy, writing quality, topical authority, backlinks, accessibility, or conventional search rank.
- No full Schema.org syntax validation or search-engine rich-result eligibility.
- No AI citation, sentiment, share-of-voice, prompt, or model-position measurement.
- No currency conversion, regional offer selection, tax/shipping interpretation, or catalog matching.
- No history database, change alert, PDF report, or ticket-system write inside this Actor.
- No proxy, login, session, cookie banner interaction, CAPTCHA handling, or robots override.
- Pages served differently by geography, consent state, or user agent may expose facts different from those this named crawler receives.
Support boundary
Support covers the documented input validation, public network-safety checks, supported static-HTML
fact extraction, score/finding contract, schemas, Dataset/KVS routing, and
result-found billing invariant for the current build.
Support cannot guarantee a target site's uptime, permission, HTML shape, server response, continued publication of any field, or placement in an external AI answer. It cannot provide legal advice, bypass target controls, recover facts available only after client JavaScript, or reinterpret unsupported entity types as though they were audited.
For a useful support request, include Actor run ID, exact URL, observation timestamp, status and reason or finding code, and a concise statement of expected versus actual static-HTML behavior. If the problem appears only in browser-rendered DOM, say so explicitly.
Built by zinin. The product is intentionally narrow: public evidence in, defensible GEO action out.