How the Overfront AI Visibility Score works
This document is the public definition of the score (REQ-030). It lives in the same repository as the implementation (
api/src/core/score.ts) and any change to the formula updates this file in the same commit.
What we measure
For a domain d and an AI engine e, we run a versioned set of discovery
prompts — questions a user who does not know the brand would ask — and check
whether the engine’s grounded answer cites or mentions the domain.
- Linked citation : the answer carries a citation link that resolves to the domain (eTLD+1 match). Weight 1.0.
- Unlinked mention : the brand or domain appears in the answer text without a link. Weight 0.3 (it creates no traffic, so it counts less).
Prompt weights
Each discovery prompt carries a weight reflecting how likely real users are to ask it. Prompts derived from real autocomplete suggestions are weighted by their autocomplete rank (a popularity proxy): the top suggestion gets the maximum weight (currently 2.0), decaying linearly toward the base weight. Prompts generated without a matching suggestion keep the base weight 1.0.
Formulas
With w_p the weight of prompt p :
CitationRate_e(d) = sum(w_p over cited prompts) / sum(w_p over all prompts)
EffectiveCitationRate_e(d) = sum(w_p x (linked x 1.0 + unlinked mention x 0.3)) / sum(w_p)
RunScore(d) = 100 x sum_e( w_e x EffectiveCitationRate_e(d) ) (integer, 0-100)
AI Visibility Score(d) = mean of RunScore over all measurements in the
last 35 days (integer, 0-100)
The displayed score averages successive measurement runs. AI answers vary
between runs even for identical prompts, so a single snapshot is noisy; the
rolling mean smooths answer-side variance while the reused prompt plan keeps
runs directly comparable. The number of runs behind a score is exposed as
runs in the API response. Note that the per-engine breakdown in the API
response reflects the LATEST run only, so it does not arithmetically
reconstruct the averaged score once more than one run is in the window.
Small samples
At the sample size one run affords, the plain rate cannot tell “never cited” from “cited rarely”: a site with a true appearance rate of 11% returns zero citations from 10 prompts about 31% of the time, and the plain rate then reports exactly 0 for a site that is not absent at all.
Where a prior has been fitted, the displayed score is therefore the posterior mean of a Beta prior updated with the run’s effective sample size, rather than the raw ratio:
Displayed(d) = (rate x n_eff + alpha) / (n_eff + alpha + beta)
alpha and beta describe the distribution of appearance rates across all
domains we have measured, and are estimated from those measurements — never
chosen by hand. Until enough domains have been measured for that estimate to
mean anything, both are empty and the displayed score is exactly the observed
rate. The reported interval always describes the observed rate, so the score
is a point estimate inside a range that is stated honestly either way. Brand
scores are not shrunk: branded prompts are a different population from the one
the prior is fitted on.
Uncertainty
Scores come with an approximate 95% interval (scoreLow / scoreHigh in the
API response): a Wilson score interval computed on the effective sample size
of the weighted prompt set, scaled by the number of runs in the window. At
the default 10 prompts a single run’s interval is wide by design — treat a
first score as an estimate, not a verdict; it narrows as weekly runs
accumulate.
Sampling rules
- If every prompt in the first half cites the domain, the remaining prompts are skipped: they cannot change the rate.
- An empty first half does not stop the run. It used to, and that halved the sample for exactly the sites least likely to be cited: at a true appearance rate of 11%, five prompts come back empty 56% of the time and ten only 31%. Half the zeros on screen were sample size, not absence.
- Mid-range results (score 30-70), where a single run is noisiest, trigger one extra full measurement run automatically; the rolling mean of the two runs roughly halves the variance. All such runs pass through the same cost-budget controls as any other measurement.
What the score is conditional on
Every measurement call forces the engine to run a web search. That removes a large source of run-to-run variance and keeps the cost of a run predictable, but it also means the score answers a conditional question:
Given that the engine searched the web, how often does it cite this domain?
It is not the unconditional chance that a user asking this question sees the domain. Published measurements find that a large share of ChatGPT repetitions never activate web search at all, so the unconditional figure is lower than the score reported here. We force search rather than sampling the activation rate because an unforced sample would mix two different sources of variance — whether the engine searched, and what it cited once it did — into one number that moves for reasons a site owner cannot act on.
Mention detection
Text mentions are matched after Unicode NFKC normalization with word-boundary protection for ASCII names and substring matching for scripts without word boundaries (e.g. Japanese). The detector is versioned; every measurement records the detector version so that score shifts caused by detector changes are distinguishable from real visibility changes. Citation position (where in the answer’s citation list the domain first appears) is recorded but not part of the score.
Current engine weights w_e : ChatGPT (OpenAI) = 1.0. Weights will be based on
public AI traffic share data as more engines are added.
Brand prompts are scored separately
Prompts that contain the brand (“Is X trustworthy?”, “X reviews”) measure awareness and sentiment, not discoverability. They are shown as a separate Brand Score and are never included in the AI Visibility Score.
The same rule applies to user-registered prompts: site owners can register a small number of questions they care about, and these are measured alongside each run and reported per prompt — but they never enter the AI Visibility Score, because self-chosen questions would let a site score against a test it wrote itself.
Method notes
- Prompts are generated from category vocabulary and real autocomplete suggestions, then filtered so no brand or site-specific wording leaks in. The prompt set is versioned; the version is shown with every score.
- Candidate prompts are additionally ranked against aggregate style
statistics (length quantiles and phrasing rates) of 23,642 discovery-style
questions drawn from real user-AI conversations, and the candidates closest
to how real users phrase such questions are selected for measurement.
Sources : WildChat-1M (used under ODC-BY, attribution to the Allen
Institute for AI) and LMSYS-Chat-1M (used under the LMSYS-Chat-1M Dataset
License). Only aggregate statistics are stored — no conversation text. The
calibration artifact is versioned (currently
prompt-style-v1); both corpora were collected in 2023, so a phrasing drift toward current usage remains a known limitation. - Measurements are stateless API calls: no conversation history, no account memory, no personalization. Real users’ personalized answers may differ.
- Cached results are shared across all users and refreshed on a 7-day cycle.
- A domain’s prompt plan (classification + generated prompts and weights) is reused for up to 30 days, so successive measurements of the same domain are directly comparable over time.
- Current sample size : 10 discovery prompts per engine per domain (the value
is configurable and shown with the score’s prompt set version). At this
sample size a single run’s 95% interval is wide — around +/-25 points near
mid-range scores — which is exactly what
scoreLow/scoreHighreport; accumulating weekly runs narrows it.