CrawlPodScan your site

Competitor gap analysis

CompetitorAnalyzer, added in 0.8.0, runs seven evidence-backed gap-reason detectors against a MeasurementReport and emits only the reasons the data actually supports — sorted by impact, each with concrete evidence and an action item. Nothing is fabricated when the data doesn't support a reason.

The seven gap reasons

ReasonWhat it detectsTypical action item
citation-gapCited by more independent sourcesBuild presence on review sites and comparison platforms
prompt-coverage-gapAppears in more prompt clusters than youCreate content targeting the clusters where you have no presence
recommendation-gapRecommended more oftenStrengthen unique value proposition signals
engine-specific-gapInvisible on one engine while visible on anotherEngine-specific — e.g. strengthen citable, well-structured content for retrieval-based engines
position-gapListed first when both are mentionedImprove brand authority and E-E-A-T signals
comparison-content-gapBenefits from comparison content you don't haveCreate a comparison page: "Brand vs Competitor"
review-social-proof-gapCited from review/forum sources you aren'tBuild review profiles on G2, Capterra; engage on Reddit

Each detector only fires when the underlying data actually shows the competitor ahead — position-gap, for example, requires at least 3 co-mentions in the same responses before it'll report a listing-order pattern at all, and only fires if the competitor is listed first more than half the time. A competitor beating you on paper doesn't automatically produce seven reasons; most real reports return two or three.

Impact classification

Every GapReason carries a GapImpact of "high" | "medium" | "low", from two exported classifier functions:

import { classifyImpactByRatio, classifyImpactByPercentGap } from "ai-visibility/competitor";

classifyImpactByRatio(yours: number, theirs: number): GapImpact       // e.g. citation-gap, recommendation-gap
classifyImpactByPercentGap(gap: number): GapImpact                     // e.g. prompt-coverage-gap, comparison-content-gap

Both are exported directly so a consumer building its own reporting on top of MeasurementReport data can reuse the same thresholds instead of re-deriving them.

CLI: compare

npx ai-visibility compare --from report.json

Prints "Why they're winning": each competitor's visibility gap (theirs - yours) and its ranked reasons grouped by impact (high/medium/low), each with its action item. Like citations, --from <file> loads a previously saved measure --json report instead of re-querying engines — without --from, --competitors defaults to every competitor already present in the loaded report.

Programmatically:

import { CompetitorAnalyzer } from "ai-visibility/competitor";

const report = CompetitorAnalyzer.analyze(measurementReport, "Acme CRM", ["HubSpot", "Pipedrive"]);

report.competitors        // CompetitorGap[], sorted by visibility.gap descending
report.overallGap.averageGap
report.overallGap.biggestGap   // competitor name, or null

Uses the same MeasurementReport as citation analysis— run both against one saved report to see where competitors are cited from and why they're winning, without querying engines twice. Or run everything at once with the full report pipeline.