The Receipts Engine

Anyone can estimate. We count.

The Receipts Engine is the scanner and monitoring stack behind everything Answerable sells. It asks real AI engines real buyer questions, stores every answer word for word, and counts the mentions with a regular expression. No model is ever asked to produce a number. The formula that turns those counts into a score is published on this page, in full, including the parts that are not flattering.

Every number on this page can be checked in the product. Each claim below ends with the command, file or file path that proves it, and the 300-business dataset we published is a file you can download and recompute without asking us for anything.

The pipeline

How a score is made.

Seven stages, in the order the scanner runs them. Three of them involve a model, and this page says which three.

  1. 01

    Classify

    Work out what the business is and where it operates

    The scanner fetches the homepage and puts it to a model in one structured call, which returns three things: a category, a geography, and the brand names the business actually trades under. This is the first of the pipeline's three model calls, and it is worth naming because its brand-name list is what becomes the regular expressions in stage 04. The model proposes the names; the matching that produces every count stays deterministic.

    That pair of category and geography becomes a cohort key, normalised to lower case, which is how businesses selling the same thing in the same place end up sharing a question set.

    // cohort.js cohortKey
    cohort_key = "plumber|melbourne"
    src/lib/classify.js classifyBusiness, src/lib/cohort.js cohortKey
  2. 02

    Generate the questions

    Write buyer questions that never contain the brand

    The questions are written for the category and the geography, not for the business, because a question that names you cannot test whether an engine names you unprompted. Two paths generate questions and the rule is kept differently on each, so it is worth being exact about which.

    On the per-domain path the rule is enforced twice: it is in the instruction sent to the model, and a mechanical filter afterwards drops any question containing the domain or its stem. On the cohort path there is no single brand to filter against, so the questions are brand-neutral by construction instead: the function is given a category and a geography and is never passed a domain at all. Either way the scrub happens before anything is asked, so a stored transcript quotes exactly what was sent.

    // classify.js classifyBusiness, the per-domain filter
    .filter(q => q
      && !q.toLowerCase().includes(stem)
      && !q.toLowerCase().includes(domain.toLowerCase()))
    
    // the deterministic cohort fallback set, verbatim
    `What are the best ${category} in ${geo}?`
    `Who is the most trusted ${category} in ${geo}?`
    `Which ${category} in ${geo} do people recommend most?`
    src/lib/classify.js classifyBusiness (the filter above), cohortBuyerQuestions and templateCohortQuestions (the cohort path)
  3. 03

    Ask the engines

    Put the questions to live AI engines and keep the answers

    ChatGPT is asked through the OpenAI Responses API with the web search tool enabled. Gemini is asked through the Generative Language API with Google Search grounding. Both are ordinary paid or free-tier API access, nothing privileged. The answer text and its citations are written to storage exactly as they came back.

    If Gemini's grounding is refused or its daily quota is exhausted, the question is re-run ungrounded. It is never quietly upgraded to a paid engine to hide the gap.

    Being exact about where that fallback is recorded: today it is counted in the scan API's own response, as a grounded and ungrounded tally in the run metadata, and it is not yet written onto the report page. The report's methodology note currently describes the direct verdict as grounded whether or not the fallback fired, which means on a quota-exhausted run the report is more confident than the run was. That is a defect in our reporting, not a nuance, and the fix is to carry the flag through to each stored answer. It is written here rather than left for someone else to find.

    src/providers/gemini.js ask, src/lib/cohort.js gemini_grounding, src/lib/scan.js assembleReport
  4. 04

    Match

    Decide whether the business was actually named

    Matching is a regular expression over the stored text, and it is deliberately fussy about short names. A brand of six characters or fewer with no space only matches when it is capitalised or in all capitals, because short brand names are usually ordinary English words too. Longer or multi-word names match case-insensitively.

    The rule exists for a real business: the brand GLOW is also a common noun. Without the rule, the phrase "a healthy glow" would score as a brand mention.

    // mentions.js matcherFor
    name.length <= 6 && !name.includes(" ")
      → /\b(GLOW|Glow)\b/     case-sensitive
    otherwise
      → /\bAustralian Glow\b/i case-insensitive
    
    analyze("she had a healthy glow")  → mentioned: false
    analyze("Try GLOW for reviews")     → mentioned: true
    src/lib/mentions.js matcherFor, analyze
  5. 05

    Count

    Increment two integers, and nothing else

    For each question the scanner increments asked, and increments mentioned if the matcher hit. A citation to the business's own domain counts as a mention for that question even when the name is not written in the prose, because an engine that links you has named you. It is also recorded as its own cited flag, so citations can be reported separately from prose mentions. Because every question is its own provider call, a citation can only ever count for the question it was returned against.

    These two integers are the 70 per cent term of every engine score. No model touches them.

    // mentions.js countMentions
    countMentions("no name here",
      ["https://glow.com.au/x"], matchers)
      → { mentioned: true, cited: true }
    src/lib/mentions.js countMentions, citedIn; src/providers/index.js runOne
  6. 06

    Site checks

    Three HTTP requests, four checks, fixed points

    Site readiness has no model in it at all. The scanner makes three plain GET requests, to the homepage, to robots.txt and to llms.txt, and awards fixed points for four checks. The crawlers it looks for in robots.txt are GPTBot, ClaudeBot, Claude-Web, PerplexityBot, Google-Extended and Bytespider. Anyone can reproduce a site readiness score by hand.

    CheckWhat passesPoints
    AI crawlers allowedSite reachable and robots.txt blocks none of the six40
    Structured dataAt least one JSON-LD block on the homepage25
    Meta basicsHomepage has both a title and a meta description20
    llms.txtA non-empty llms.txt that is not an HTML error page15
    src/lib/sitechecks.js AI_BOTS, runSiteChecks
  7. 07

    Assemble

    Apply the formula, and write down what actually ran

    The counts and the site score go through the published formula. Two model-graded inputs join them here: a recognised flag and a sentiment value, both read from the stored direct-answer transcript. Together they can move an engine score by at most 30 of 100 points.

    The report then records its own methodology in a field, not just in the marketing: which engines were asked a fresh direct question, which were scored from a shared category sweep, and which were not configured at all.

    // report.methodology
    { note, tier, weights,
      providers_scored, direct_platforms }
    
    // per engine
    platforms[].direct_asked → true | false
    src/lib/scan.js assembleReport, src/lib/judge.js judgeEvidence

Published in full

The formula.

A black box is a business decision, not a technical necessity. Here is ours, in the arithmetic the code actually runs.

Per-engine visibility, out of 100

rate   = asked > 0 ? mentioned / asked : 0
sent01 = clamp(0, 1, (sentiment + 1) / 2)

platform_visibility = round(100 * (
    0.70 * rate
  + 0.20 * (recognized ? 1 : 0)
  + 0.10 * sent01
))

70 per cent is a count. The share of asked questions in which the engine named the business, where asked only counts calls that actually succeeded. 20 per cent is recognition, a true or false flag for whether the engine knew the business when asked about it directly. 10 per cent is sentiment, a value from minus one to one, rescaled to sit between zero and one.

The recognised flag and the sentiment value are the only model-produced inputs in the whole pipeline. They are graded from the stored transcript, and between them they are worth at most 30 of the 100 points. We say that plainly rather than letting the number read as fully mechanical, because the honesty here is asymmetric: the counted 70 per cent can be reproduced by a third party from the stored answers, and the other 30 cannot.

Overall score, out of 100

// engines that are configured AND answered
scored = platforms.filter(p => p.configured && p.ok)

if (scored.length === 0)
  overall = round(site_readiness * 0.25)
else
  overall = round(
      0.75 * mean(platform_visibility over scored)
    + 0.25 * site_readiness
  )

Three quarters of the score is what the engines said. One quarter is whether the site is technically readable. That weighting is a judgement call and it is stated openly so it can be argued with.

Status bands

score >= 70          → "Visible"
score >= 35          → "Partly visible"
score > 0 or recognised → "Weak"
otherwise            → "Not found"

Where each line lives

PieceFile
Engine scorescore.js platformScore
Overall scorescore.js overallScore
Weightsscore.js WEIGHTS
Bandsscore.js statusFor
Site pointssitechecks.js checks
How to check this

Take the two engine scores and the site readiness score from any row of our published dataset and run the arithmetic above. It reproduces the published overall score. We ran it across every row: 300 of 300 match exactly, with no mismatches.

Four decisions

Four things that make it different.

Each one is a design decision with a cost attached, and each one ends in something a sceptic can run.

The cohort engine

One live sweep, shared by a whole category

Businesses selling the same thing in the same place get asked the same brand-neutral buyer questions, and the live answers to those questions are shared between them. Every business is then scored by re-counting that same shared transcript against its own matchers. The cohort questions are generated from a category and a geography only and never see a domain, so sharing them changes nothing about what is being measured.

The call count is emitted by the code rather than estimated, and the conditions belong with the number. Measured locally with MOCK=1 and all five adapters enabled, six businesses in one cohort came to 31 provider calls, against the 180 the same six would cost as separate full scans. On the deployed two-engine configuration the same comparison is 16 against 72. Cohort transcripts expire after 14 days, at which point the sweep is re-run live.

// emitted locally, MOCK=1, 6 domains, one cohort,
// all five adapters reporting configured
provider_calls = { buyer_sweep: 25,
                   direct: 6,
                   total: 31 }

// deployed today: 2 engines configured
// 5 questions x 2 + 6 directs = 16, vs 6 x 12 = 72
How to check this

Run the scanner locally with MOCK=1 and post six same-cohort domains to /api/scan-batch. The response carries meta.provider_calls and cohorts[0].created. Post three more in the same cohort inside the 14-day window and the sweep count comes back zero.

Pinned question sets

Month to month only compares if the question does not move

A scanner that re-derives its buyer questions from a model every month is comparing two different measurements and calling the difference progress. Our monitoring product pins the question set on the first scan, stores each question under a hash of its own text, and re-verifies after every scan that the scanner asked that exact set. If it did not, the question set version is bumped and the comparability gate fires.

The scanner accepts a pinned set from an authenticated caller and stores it under a content-hashed cohort key, so a pinned run never collides with the shared cohort. An invalid question set is rejected with a 400 rather than silently falling back to generated questions.

How to check this, and what we do not yet claim

Locally, posting a pinned set returns a report whose questions are byte-identical to what was sent, under a cohort key such as plumber|melbourne#q87454eae. This path is built and verified in local runs. The monitoring product has not been deployed, so we do not present monthly pinned questions as a live production guarantee.

The comparability guard

A broken scan can never be reported as an improvement

There is a specific failure that flatters an agency. The mention rate is mentions divided by questions asked, and asked only counts calls that succeeded, so when one engine goes dark the denominator shrinks and the score goes up. Left alone, a client whose scan half broke would be emailed an improvement.

The engine classifies that move as an artefact and it never reaches the email. An engine going dark is not recorded as a visibility loss and an engine coming back is not recorded as a win: both are disclosed to the client as a reading problem. A scan that failed cannot carry a score at all, and that is enforced by a database constraint rather than by convention. If the evidence behind a scan cannot be stored in full, the scan is demoted to failed and its score is deleted.

How to check this

Run the diff test suite in the monitoring codebase. The block named "the fake gain: a broken scan must never read as an improvement" asserts that the raw score really does rise 8 points when Gemini dies, that the move comes back classified as an artefact, and that no score-moved item is emitted. All 28 assertions in that suite pass.

Counted, not generated

No model is ever asked for a number

Three structured model calls exist in the scanner: one classifies the business and proposes its brand names, one generates the cohort's buyer questions, and one grades the stored transcript for the recognised flag, the sentiment value and the report's prose. None of the three emits asked, mentioned or visible. Every number in a report traces back to a counter incremented in the provider loop.

Every answer is stored word for word. The report page shows three in full and the rest as locked rows, never blurred; the free scan on this site shows the direct answer about you and tells you how many are withheld. The full set is what the audit unlocks. In the monitoring product each stored answer also carries a SHA-256 of its own text, so it can be shown that nothing was edited after the fact. An answer that comes back byte-for-byte identical to last month is excluded from wins and losses before classification, because identical text is evidence of a cache, not evidence of stability.

How to check this

On any report page, read the three featured answers against the questions they were counted from; the locked rows show you the question and withhold only the answer. Then grep the scanner for a model call that returns a score. A grep returns exactly three JSON-schema calls, at classify.js classifyBusiness, classify.js cohortBuyerQuestions and judge.js judgeEvidence, and not one of them produces a count.

The boundaries

What we do not claim.

This is not fine print. A measurement is only worth something if its limits are stated by the people who built it, so these are ours, in the same size type as everything else.

  • Two engines, not five

    The deployed scanner queries ChatGPT and Gemini. Adapters for Claude, Perplexity and Grok exist in the code but have no API key configured, so a report marks them Not configured rather than scoring them. The scanner publishes a live configuration endpoint at ai-visibility.jackson-89e.workers.dev/api/config, so which engines are switched on is a fact anyone can re-check at any time rather than a claim on a marketing page. That is the scanner's own host, not this one: this site is static pages and the endpoint lives with the code that answers it.

  • No partnership with any AI company

    We are a paying API customer of two AI providers. We have no partnership, integration, affiliation or authorised access with OpenAI, Google, Anthropic, Perplexity or xAI, and we do not display their logos as endorsements.

  • Scheduled, not realtime

    Monitoring re-scans each client once a month, on their own scan day, picked up by a daily job that runs whoever is due. Between scans nothing is measured. There is no always-on, continuous or realtime tracking, and the monitoring product is built rather than operating: it has not been deployed yet.

  • We do not see real user sessions

    We have no access to anyone's actual ChatGPT or Gemini conversations, no query volume data and no share of voice measurement. We ask a fixed set of questions ourselves, through public APIs, and count what comes back. Anyone claiming to observe what real buyers are shown by these tools should be asked how.

  • A scan is a snapshot

    AI answers are non-deterministic and personalised, so the same question can return a different answer an hour later. In the published study each question was asked exactly once, with no repetition runs, and that limit is stated on the study page and anywhere its numbers are quoted. Two scans differing is often the model being inconsistent, which is precisely why the monitoring product carries a noise band and treats volatile questions separately.

  • In the cold-outreach scan, one call is about you

    In the thin tier used for cold outreach, which is not the free scan you run yourself on this site, the only fresh per-business call is a single Gemini question about that business. The ChatGPT result comes from the shared category sweep, which is identical for every business in the cohort. A thin-tier report says exactly that in its own methodology field. The free scan on this site takes the other path: it is a full per-business run, with its own fresh questions and a fresh direct question to every configured engine.

  • The score is not a business outcome

    Nothing in either codebase measures leads, revenue, traffic or rankings, so we do not claim the score predicts or causes any of them, we do not promise a score will improve, and we attach no timeframe or number to any improvement. We also do not sell llms.txt as a ranking lever: it is 15 of 100 points in our own readiness check, and Google has stated it does not use the file.

  • We do not claim to know why a number moved

    The monitoring engine attributes a score move to the components it tracks, which are rate, recognition, sentiment and the site checks. When none of those flipped, it refuses to name a cause rather than inventing one, and it never attributes a change to an AI algorithm update.

  • Report links are private, not a public register

    Individual report pages are unlisted, unguessable and set to noindex by design. They are proof for the business that owns them, not a directory a third party can look a competitor up in.

Proof at scale

The engine, run in public.

The strongest thing we can say about the measurement is that we ran it on 300 businesses that are not our clients and published the raw file.

300Australian businesses scanned
6Industries, 50 each
300 / 300Rows that reproduce exactly
CC BY 4.0Licence on the raw data

The State of AI Visibility in Australia 2026 scanned 300 businesses on 30 July 2026, 50 in each of six industries, and published the complete dataset as CSV and JSON under a CC BY 4.0 licence with Answerable named as publisher. It is the same engine described on this page, pointed at businesses with no relationship to us.

The study states its own limits on its own page. Each question was asked once, with no repetition runs. All 300 businesses have a ChatGPT record but only 137 have complete records from both engines, and that figure is countable in the published file. The sample frame was top organic search results plus recognised national anchor brands, which is a visible-end sample rather than a random or representative one.

It also publishes a correlation that works against the obvious sales pitch. Across the 137 complete records, site readiness against mean per-engine visibility comes out at a Pearson r of 0.116. Readiness explains almost nothing about who gets recommended, and the study says so in those terms rather than burying it.

The reason to lead with this file is simple. A sceptic, a journalist or a regulator can recompute every headline number in the study from the raw data with four lines of arithmetic and no access to us at all.

// recompute the overall score from published columns
const v = ['chatgpt', 'gemini']
  .filter(p => r[p + '_questions_asked'] != null
            && r[p + '_questions_asked'] !== '')
  .map(p => r[p + '_visibility_score']);

Math.round(0.75 * (v.reduce((a, b) => a + b) / v.length)
         + 0.25 * r.site_readiness_score)
  === r.ai_visibility_score   // true for all 300 rows

Licensed CC BY 4.0. Use it, quote it, check our arithmetic with it. Attribution to Answerable is all we ask. The headline figures are also broken out in citable form in the AI visibility statistics summary.

Questions

The questions people ask about the measurement.

How is an AI visibility score calculated?
Each engine gets a score out of 100 from a fixed formula: 70 per cent is the share of asked questions in which the business was named, 20 per cent is whether the engine recognised the business when asked about it directly, and 10 per cent is the sentiment of that direct answer, rescaled to sit between zero and one. The overall score is 75 per cent of the mean engine score plus 25 per cent of a site readiness score built from four deterministic checks. The whole formula is published on this page.
Which AI engines does Answerable query?
Two today. ChatGPT, through the OpenAI Responses API with the web search tool, and Gemini, through the Generative Language API with Google Search grounding. Adapters for Claude, Perplexity and Grok exist in the code but have no API key on the deployed scanner, so a report marks them Not configured rather than scoring them. The scanner publishes a live configuration endpoint on its own host, ai-visibility.jackson-89e.workers.dev/api/config, so that which engines are switched on stays checkable rather than becoming a stale marketing claim.
Is a model deciding my score?
No model is ever asked to produce a number. The 70 per cent term is a count: mentions are matched by regular expression over the stored answer text. Two inputs are model-graded from those same stored transcripts, a recognised flag and a sentiment value, and together they can move an engine score by at most 30 of 100 points. We state that split rather than hiding it, because the counted part is the part a third party can reproduce and the graded part is not.
Do you see what real people are asked in ChatGPT or Gemini?
No. We have no access to anyone's actual ChatGPT or Gemini sessions, no query volume data and no share of voice measurement. We ask a fixed set of buyer questions ourselves, through public APIs, and count what comes back. AI answers are non-deterministic and personalised, so a scan is a snapshot of what those engines said to those questions at that moment, not a sample of what every buyer sees.
Can I check your numbers myself?
Yes, and that is the point of the name. The scoring formula is published above, and the raw data behind our 300-business study is downloadable as CSV and JSON under a CC BY 4.0 licence. Recomputing the overall score from the two published engine scores and the published site readiness score reproduces all 300 rows exactly. Nothing in that check requires access to us.
Does a higher score mean more leads?
Nothing in the scanner measures a commercial outcome, so we make no claim that the score predicts or causes leads, revenue, traffic or rankings, and we do not promise that a score will improve. Our own published study found site readiness correlates only weakly with per-engine visibility, at a Pearson r of 0.116, and we published that result even though it argues against the easiest thing we could sell.

See what the engine says about you.

The free scan runs the same pipeline described on this page and gives you the direct answer it counted, word for word. If you want the full read on where the gaps are, the audit is the next step.

Run my free scan