CrawlPodScan your site

GEO scoring methodology

ContentAnalyzer (used by ai-visibility audit/lintand this package's programmatic API) scores a page's content citability across seven fixed-weight dimensions that sum to exactly 1.0 — no machine learning, no hidden factors. The weights are published as data, not just documented, so other surfaces can consume the same numbers instead of maintaining their own copy.

This is a different score from the one on /scan. See Two different scores below before assuming a number here and a number there should match.

The seven dimensions

DimensionKeyWeightWhat it checks
Answer placementanswerFrontLoading0.20Whether a direct answer to the page's topic appears near the top, where AI systems weight content most heavily.
Authority signals (E-E-A-T)eeatSignals0.20Author, organization, contact, and trust-signal markup — what separates “extractable” content from “citable” content.
StructureheadingStructure0.15A single H1 and a consistent, unskipped heading hierarchy, which is what makes a page machine-segmentable.
Structured dataschemaCoverage0.15Valid JSON-LD structured data, the most direct machine-readable signal a page can offer.
Factual densityfactDensity0.10Concrete numbers, dates, and statistics per 100 words. The most heuristic of the checks, weighted accordingly.
Semantic claritysnippability0.10Whether each section under a heading stands alone with enough context to be quoted or excerpted independently.
Crawler accessibilitycrawlerAccessibility0.10Whether AI crawlers are actually allowed to fetch the page at all (meta robots, robots.txt, llms.txt) — a gate more than a differentiator, since a hard block already zeroes out every other dimension's value.

Weights sum to exactly 1.0, enforced by a test in the package's own suite — verified directly against ContentAnalyzer.SCORING_WEIGHTS on the installed 0.5.0 package, not assumed from the table above.

Why these weights

Answer placement and E-E-A-T sit highest (0.20 each) because they determine whether an AI system can extract and trustciting the content at all — the two failure modes that make otherwise-good content uncitable. Structure and structured data (0.15 each) are the most direct machine-readability signals a page can offer, one implicit (heading hierarchy) and one explicit (JSON-LD). The three lowest-weighted dimensions (0.10 each) are lower for three different reasons, not the same one: fact density relies on the weakest heuristic (a regex-based count, not real claim-verification), semantic clarity overlaps partly with structure and E-E-A-T rather than measuring something wholly separate, and crawler accessibility is a gate more than a differentiator — a hard block already zeroes out every other dimension's value in practice, so its own weight doesn't need to carry much of the score.

crawlerAccessibility and context

Added in 0.5.0. ContentAnalyzer.analyze() takes an optional second parameter:

analyze(html: string, context?: { robotsTxt?: string; hasLlmsTxt?: boolean }): Promise<AIReadabilityScore>

Without context, this dimension can only check the page's own <meta name="robots"> tag — omitting it is fully backward compatible with pre-0.5.0 call sites, it just scores this one dimension with less information. ai-visibility audit and audit --dir both supply it automatically: a best-effort fetch of robots.txt at the same origin (or the local file, for --dir) plus an llms.txtpresence check. A failed/missing fetch is treated as "unknown", never a hard failure — see the CLI reference for a real run showing this in practice (a robots.txt blocking GPTBot surfaces as a high-severity issue on this dimension).

scoring-weights.json for non-JS consumers

Published at dist/scoring-weights.json with every release, generated from ContentAnalyzer.SCORING_WEIGHTS at build time — the same pattern as dist/crawlers.json, and for the same reason: other CrawlPod surfaces (this site's own scanner, the WordPress plugin) should align to one published source of truth instead of each maintaining a copy that can silently drift, which is exactly the failure that hit the crawler registry once already. This is the actual file shipped with 0.5.0:

dist/scoring-weights.json
{
  "schemaVersion": 1,
  "packageVersion": "0.5.0",
  "generatedAt": "2026-08-05T17:24:13.477Z",
  "source": "https://github.com/Muhammadfaizanjanjua109/ai-visibility/blob/main/src/analyzer/scoring-weights.ts",
  "docs": "https://github.com/Muhammadfaizanjanjua109/ai-visibility/blob/main/docs/scoring.md",
  "dimensions": [
    { "key": "answerFrontLoading", "label": "Answer placement", "weight": 0.2, "description": "..." },
    { "key": "eeatSignals", "label": "Authority signals (E-E-A-T)", "weight": 0.2, "description": "..." },
    { "key": "headingStructure", "label": "Structure", "weight": 0.15, "description": "..." },
    { "key": "schemaCoverage", "label": "Structured data", "weight": 0.15, "description": "..." },
    { "key": "factDensity", "label": "Factual density", "weight": 0.1, "description": "..." },
    { "key": "snippability", "label": "Semantic clarity", "weight": 0.1, "description": "..." },
    { "key": "crawlerAccessibility", "label": "Crawler accessibility", "weight": 0.1, "description": "..." }
  ]
}
fetch-and-vendor, run at the other project's own build/release time
# Pin an exact version — @latest is fine for eyeballing, not for a build script.
curl -sL https://cdn.jsdelivr.net/npm/ai-visibility@0.5.0/dist/scoring-weights.json -o scoring-weights.json

# Then transform into whatever native structure that surface needs, and commit
# the *generated* result — never fetch this URL at request time in production.

Check schemaVersion before trusting the shape, same rule as crawlers.json: fail loudly if it's higher than what your transform script was written against, rather than silently misreading a changed structure.

Two different scores

This package scores content citability. The WordPress Pro audit engine (in development) scores agentic readiness. These are not the same number, on purpose.

ai-visibility (this package)WordPress Pro audit engine
Question it answersCan an AI system cite this content in an answer?Can an AI agent operate this page directly?
Dimensions7 — answer placement, E-E-A-T, structure, schema, fact density, snippability, crawler access9 — accessibility tree, WebMCP, and agent-interaction checks; see the WordPress docs for the full list
Where it runsCLI (audit/lint), or programmatically via ContentAnalyzerThe CrawlPod WordPress plugin's Pro tier

The same page can score well on one and poorly on the other — good, citable prose with no interactive elements at all can score highly here while having nothing for an agentic-readiness check to evaluate; a product page with well-labeled interactive elements but no author information runs the reverse. Neither number is a Lighthouse or PageSpeed score, and neither is designed to be compared to the other directly — they measure different things about the same page. This site's own free scanner is a third, related but distinct system: it checks crawler access, content readability, structured data, and agentic-readiness signals together in one report, with its own published weights on the results page — see Agentic Browsingfor how that overlaps with Lighthouse's own audit category of the same name.

See the CLI reference for running this scorer from the command line, and the API reference for the programmatic ContentAnalyzer signature.