Skip to content

Case study

A GEO and AI visibility measurement platform

Built for a national consumer communications agency, under NDA. An independent consulting engagement, March to June 2026.

Role
Engineer and product lead. Methodology, security, and the client relationship.
Period
March to June 2026

What was the problem?

Brand and communications teams could see their coverage in traditional media. They had no reliable read on whether that coverage reached AI-generated answers, which sources those answers relied on, or how they compared against competitors inside the same response.

That gap matters because the question has changed. When someone asks an AI engine which skincare brand to buy, the answer is assembled from sources the brand does not choose and cannot see. A comms team can place a story in a national outlet and have no idea whether any engine ever reads it back to a customer.

Commercial GEO tooling existed. It either did not cover the engines the agency needed or could not be trusted at the volume and cadence the work required.

Why build rather than buy?

The category is real and I evaluated it honestly: Ahrefs, Semrush, Similarweb, Profound, Airops, Peec.ai. Two things ruled it out.

Engine coverage was partial. The agency’s clients needed all six engines their customers actually use, not the three or four a given vendor had wired up. A competitive share-of-voice number computed across an incomplete engine set is not a smaller version of the right answer. It is a different answer.

Collection was the hard part, and nobody was solving it the way we needed. Several vendors lean on public APIs. Public APIs do not return what a person sees. The consumer-facing surfaces of these engines produce different answers, with different citations, than their API equivalents, and the citation set is the entire point of the exercise. To measure what a customer is actually shown, you have to collect from the surface a customer actually uses.

So I built it, including the part that makes it hard: the headless browser collection layer that captures what the consumer surfaces actually show.

The architecture

Data flow of the AI visibility measurement platform Stage 1, Collection: Playwright, Apify, managed headless browser sessions. Stage 2, Scheduling and retry: 3 runs a day, unattended. Stage 3, Normalization and storage: Prisma, PostgreSQL. Stage 4, Metric computation: 5 defined, versioned metrics. Stage 5, Reporting: Next.js, python-pptx, PptxGenJS. Each stage feeds the next.CollectionPlaywright, Apify, managedheadless browser sessionsCaptures answers public APIs do not exposeScheduling and retry3 runs a day, unattendedBackoff, retry, and run integrityNormalization and storagePrisma, PostgreSQLOne shape across six different answer formatsMetric computation5 defined, versioned metricsComparable week over week, not anecdotalReportingNext.js, python-pptx,PptxGenJSDashboards and generated client decks
The pipeline, collection to reporting. The hard part is the top box, not the bottom one.

The collection layer runs on Playwright and Apify with managed headless browser sessions, which is what makes it possible to capture answers the public APIs do not expose. Everything downstream of that is comparatively ordinary engineering, and saying so is the honest framing: scheduling with retry and backoff, normalization into one shape across six different answer formats, a Prisma and PostgreSQL data model, metric computation, and a Next.js reporting application that also generates client decks programmatically through python-pptx and PptxGenJS.

I was the sole builder. There is no team behind that stack to credit.

The scale

50+
household-name consumer brands monitored
2,500+
tracked prompts
~225K
prompt executions per month
~1.35M
AI responses collected per month

50 brands x 50 prompts = 2,500 tracked prompts, x 3 runs a day = 7,500 executions a day, x 6 engines = 45,000 responses a day.

All 50+ brands were monitored concurrently, and every run queried all six engines.

What it measured

Response presence
How often an AI engine returns any answer to a tracked prompt in the brand's topic area.
Citation rate
How often the engine's answer cites the brand's own or earned content.
Share of AI voice
The brand's share of mentions across a competitor set within answers to the same prompts.
Citation and source mapping
Which publishers, owned pages, and third-party sources each engine pulls from for brand-relevant prompts, and how that mix shifts after an earned media push.
Brand descriptor shift
Change over time in the language models attach to the brand.

The two that clients reacted to hardest were citation and source mapping, because it told them which publishers actually influence the answer about them, and brand descriptor shift, because it told them what the models say they are. Those are different questions from “are we visible,” and they are usually the more actionable ones.

Security and continuity

A measurement system holding competitive data for 50+ brands is a security surface, and somebody has to own that. I did.

  • private repository handling
  • credential hygiene and secret rotation
  • two-factor enforced onboarding
  • documented least-privilege server access
  • handoff runbooks

The two-factor onboarding flow is one I built rather than one I documented. The runbooks exist because a contractor-built system that only the contractor can operate is a liability dressed as an asset, and the engagement was always going to end.

Product practice

I wrote the PRDs, user stories, and acceptance criteria for each reporting release, and made the roadmap calls on scope and sequencing.

I also shipped the reports and then sat in the meetings where clients read them, which closes the loop most reporting products leave open. When a client misread a metric, I saw the misreading happen and could change the definition, the visualization, or the surrounding explanation that same week. Most reporting products have a translation layer of account managers between the person who builds the metric and the person confused by it. This one did not, and the methodology got better faster because of it.

Alongside the reports I produced the client education layer: what each metric means, what it does not mean, and what a change in it should and should not trigger. That last one prevents more bad decisions than the metric itself enables.

What I would do differently

I let the prompt sets grow before I versioned them. Prompts were added per client as questions came up, and for the first stretch there was no clean record of which prompt set produced which week’s number. That makes week-over-week comparison quietly unreliable, which is the one thing a measurement product cannot be. I standardized a fragmented prompt library into a single versioned, tracked set partway through, and it should have been versioned from the first run.

I under-invested in collection observability early. When a run partially failed, I found out by noticing an odd number in a report rather than from an alert. For a system running unattended three times a day, run integrity deserved instrumentation from the start, not after the first surprise.

I would keep metric definitions out of the code. Changing what a metric meant required a deploy, so definitions were refined more slowly than they deserved. A definition belongs where a non-engineer can read and challenge it, with code doing only the arithmetic.

What it did for the client

The goal was never a dashboard. The agency needed to walk into brand meetings able to answer the question every client had started asking: what do AI engines say about us, and which sources do they listen to? For 50+ household-name brands, this platform made that answerable week over week, with the receipts attached: which publishers influenced the answer, how the brand compared inside the same response, and what the models called them unprompted.

That last one moved people most. A brand would ask for a visibility score, and what changed their behavior was discovering a competitor owned the descriptor they thought was theirs.

One observation for anyone building in this category: engines change their answer surfaces without notice, and every change silently breaks collection. Keeping the data flowing is an operations discipline more than a data science one, and it is what will separate the products that survive from the ones that demo well.