AI answer engines are replacing traditional search for millions of queries. Getting cited by ChatGPT or Perplexity requires different signals than ranking in Google:
- Structured data (JSON-LD) with FAQPage schema shows 2.7x higher citation rates
- llms.txt files help AI systems understand your site at a glance
- E-E-A-T signals (author credentials, trust pages) determine citation trustworthiness
- Content extractability: clean, well-structured content gets cited; paywalled content doesn't
| Factor | Weight | What It Checks |
|---|---|---|
| Structured Data (JSON-LD) | 12% | Presence of LocalBusiness, FAQPage, Service, HowTo schemas |
| Content Depth | 10% | Word count, heading hierarchy, paragraph structure, lists |
| AI Access Files (llms.txt, sitemap) | 5% | llms.txt, llms-full.txt, robots.txt, sitemap.xml availability, per-page Markdown source endpoints |
| E-E-A-T Signals | 8% | Author meta, Person schema credentials, trust pages, reviews |
| FAQ Content | 8% | FAQPage schema, details/summary blocks, question-style headings |
| Citations & Authority | 8% | External links, authoritative domains, sameAs references |
| Schema Completeness | 8% | Property depth per schema type vs recommended properties |
| Entity Consistency | 7% | Name consistency across schema, title, og:title; contact alignment |
| Content Freshness | 7% | dateModified, Last-Modified header, sitemap lastmod, copyright year |
| Content Extractability | 6% | Content-to-boilerplate ratio, citation-ready blocks, paywall detection |
| Definition Blocks | 6% | "What is", "How to" headings, step lists, HowTo schema, dl elements |
| Named Entities | 6% | Brand mentions, knowsAbout/founder signals, proper noun density |
| Snippet Eligibility | 6% | noindex/nosnippet/max-snippet directives in meta robots and X-Robots-Tag. Google ties AI feature eligibility to these (source) |
| Technical SEO | 5% | H1 presence, image alt text, meta description length, canonical tag |
| Schema Validity | 5% | Duplicate singleton @types, JSON parse errors, empty JSON-LD blocks |
| AI Crawler Access | 4% | Per-bot robots.txt rules for GPTBot, ClaudeBot, PerplexityBot, etc., plus Content Signals directives |
These are weights, not percentages of the score. They are relative and do NOT
sum to 100 (the core set sums to 111), so the table's % column is the weight,
not the share. The score divides by the actual total of the factors it scored, so
a weight-12 factor is worth about 12.4% of a typical page's score, and less on a
page where more factors apply. Read sharePct on each factor for its real share;
it always sums to 100 for one report. Pass --factors <list> to run a subset (see
the CLI reference), which changes the total
again.
A factor the analyzer reports as not applicable to a page is left out of the score
entirely, weight and all, so a product page is not marked down for having no FAQ.
Its sharePct is 0.
These are excluded by default; when included, the weights renormalize.
- Geographic Signals (7%): LocalBusiness geo data, address, areaServed. Enable with
--include-geo. - Agent Skill Exposure (6%): Schema.org Action, MCP, A2A agent cards, form affordances. Enable with
--include-agent-skills. - Lighthouse (8%): Performance, Accessibility, and Best Practices scores via Google PageSpeed Insights (mobile strategy). Enable with
--lighthouse. Adds ~15-30s per audit; setPAGESPEED_API_KEYto lift anonymous rate limits.
Note on Google's guidance. Google's AI features and your website guide says
llms.txtand heavy structured data aren't required for AI Overviews or AI Mode. We still score them: Google is one engine; ChatGPT, Perplexity, and Claude do rely on them. Snippet eligibility is the one hard gate Google enforces: a page must be indexable and snippet-eligible to appear in AI features.
The audit emits a 0–100 score for every factor and a weighted overall score (0–100) — there are no letter grades or pass/partial/fail labels. Threshold the raw score to whatever bands suit your use. For reference, the CLI's own conventions:
| Score | Meaning |
|---|---|
| 70–100 | Strong — meets the CLI's pass gate |
| 40–69 | Moderate — clear gaps remain |
| 0–39 | Weak — major work needed |
The CLI exits 0 for an overall score ≥ 70 and 1 below; see Exit codes.
A sitemap (or static) run averages each factor across every audited page into crossCuttingIssues[], and the worst of those plus the per-page critical defects become the ranked prioritizedFixes[]. Two refinements keep that rollup honest:
Best-page context on every factor. Each cross-cutting issue carries bestScore and bestPageUrl — the single highest-scoring page for that factor (homepage wins ties). A site-wide gap then reads as "Structured Data is 100 on the homepage — propagate that template to the 393 other pages" rather than a bare "add schema".
Two averages, because there are two questions. A factor that only some page types can satisfy has a structurally low site-wide mean: an FAQ living on 8 of 500 pages averages ~1/100, which is arithmetically correct and describes a site that doesn't exist. Every cross-cutting issue therefore carries both:
| Field | Denominator | Answers |
|---|---|---|
avgScore |
every audited page | How much of the site has this? |
applicableAvgScore |
pages the factor applies to (applicablePages) |
How good is it where it exists? |
affectedPages / applicableAffectedPages split the same way. Reports show the applicable figure; avgScore keeps its original meaning and value so existing consumers are unaffected.
How applicability is decided. An analyzer that can tell reports it directly by returning applicable (FAQ Content and Definition Blocks do, from schema, URL, title, and page structure). For any analyzer that stays silent, a factor expected site-wide always applies, and a page-specific one applies where it is present (score ≥ 30) — the pre-existing rule, so silence preserves the old behavior exactly.
Page-specific factors aren't site-wide failures. Some factors legitimately apply to only certain page types — a product or portfolio page has no business carrying an FAQ or a glossary Definition Block, so a 0 there is correct, not a gap. Averaged across a whole site, these score near 0 and "affect" almost every page, which would otherwise float them to the top of the fix list and read as "Critical: build an FAQ" even when the site already has a good one on /faq. Each cross-cutting issue therefore carries a status:
status |
Meaning | Ranking |
|---|---|---|
sitewide |
Expected on every page (schema, E-E-A-T, freshness, citations…); a low average is a real coverage gap. | By prevalence, as before. |
limited |
A page-specific factor present on at least one page (best score ≥ 30) but isolated. A tune-up/extend, not build-from-scratch. | Demoted below all sitewide issues. |
opportunity |
A page-specific factor not yet present on any audited page. Adding it is discretionary. | Demoted, with no pages marked "affected". |
For a limited factor the fix is scoped to the page(s) that actually carry it and the recommendation is the tune-up from there ("add question-style headings to /faq") — never the "add it everywhere" recommendation aggregated from pages that correctly lack it. Presence (best ≥ 30), not coverage breadth, is the gate: thin coverage is the expected state for these factors and never downgrades a limited to a worse label. The page-specific set is currently FAQ Content and Definition Blocks (PAGE_SPECIFIC_FACTOR_IDS, exported from @canonry/aeo-audit/scoring).