Here is the workflow we use to turn that broad question into an AI visibility monitoring setup based on realistic prompts, repeated testing, and business context.

 

“Are we visible in AI?” is not one question

More clients are asking us how their brands appear in AI-generated answers, so we get the usual question: Are we visible?

But once we start discussing what they actually want to know, that one question quickly turns into four different ones:

These questions are related, but they do not measure the same thing.

A brand can be mentioned frequently and still be described inaccurately. A website can be cited without the brand being recommended. A competitor may appear more often, while your brand receives a stronger position when it does appear.

That is why we do not treat AI visibility as a single score.

Before measuring anything, we need to define which part of visibility we are trying to understand.

What happens when an AI platform answers a prompt?

AI-generated answers do not work like a stable list of search results.

The exact process differs between platforms, but a web-grounded answer roughly goes through several stages.

The platform receives the user’s prompt and starts with the information already available within the model. That initial knowledge can already introduce bias toward certain brands, sources, or commonly mentioned entities.

When web search is involved, the platform may then break the prompt into several related searches. This is usually referred to as query fan-out.

The system retrieves relevant passages from search results, filters and re-ranks them, and then generates an answer using a relatively small number of selected sources.

So, in simplified form, the process looks like this:

Input prompt  →  initial model knowledge  →  query fan-out searches  →  passage retrieval  →  re-ranking  →  grounded answer with a few citations

A single AI answer can mention several brands while relying on only a small set of cited sources

 

This matters because the final answer depends on more than traditional rankings: what the model already knows, which searches it generates, which passages it selects, and which sources survive the final filtering stage.

The same prompt can therefore produce different brand recommendations, rankings, descriptions, and citations across multiple runs.

One answer is not a dataset.

The AI visibility measurement problem

With Google Search, we can start with search queries, impressions, clicks, and search volume estimates.

We do not currently have the same kind of usable query-level demand data for AI platforms. There is no equivalent report showing how many times users asked ChatGPT, Gemini, or Claude a particular question during the previous month.

That makes synthetic prompt tracking the most practical way to monitor AI visibility.

In simple terms, we create a defined set of prompts, run them through selected AI platforms, and track how the answers change over time.

Many AI visibility tools already monitor large sets of pre-selected synthetic prompts. That is not useless, and it can provide a broad view of category visibility and general brand presence.

The problem is specificity!

Generic prompt sets often do not represent the products, markets, modifiers, comparison criteria, or customer questions that matter to a particular business. And if the prompt set does not reflect how people choose within your category, the resulting visibility score can be technically precise but practically irrelevant.

So, we build the prompt set around the business rather than starting with whatever prompts happen to be available in some SEO tool.

Our AI visibility monitoring workflow

1. Start with the data you already have

We do not start by asking an AI tool to generate hundreds of random prompts. Our existing sources already tell us something about customer demand and intent.

Google Search Console

Search Console contains queries people already use to find the website. These queries help us identify relevant products, problems, categories, use cases, and comparison terms.

They do not tell us exactly what people ask on AI platforms, but they show which topics already carry real search demand.

SEO keyword tools

Keyword research tools help expand the initial dataset with related topics, modifiers, alternatives, comparison terms, and product attributes.

This is especially useful when we need to cover different stages of the decision-making process rather than only the keywords for which the website already receives impressions.

Customer support

Customer support questions show how people describe their needs before making a decision.

These questions are often more conversational than traditional search queries, which makes them particularly useful when preparing prompts for AI platforms.

Search data tells us what people look for, but customer support often tells us how they ask. Together, these sources give us a grounded starting point for the prompt set.

 

2. Turn search queries into realistic prompts

Next, do not paste a keyword list into an AI visibility tracker. Search keywords need to be converted into questions that resemble natural AI interactions.

Keyword research helps identify the topics, modifiers, and comparison terms that should shape a realistic prompt set

 

For example:

Search query: best drip coffee maker
Prompt: What are the best drip coffee makers?

Search query: manual espresso machines
Prompt: Which manual espresso machines would you recommend?

Search query: best espresso machine under 500
Prompt: What’s the best espresso machine under $500?

Search query: automatic coffee machines
Prompt: What are the best automatic coffee machines?

We also keep the relevant modifiers. Price range, product type, use case, audience, and comparison criteria can significantly change the brands and sources included in an answer.

The goal is not to predict every possible way someone might phrase a question. That would be impossible. We need a consistent prompt sample based on real demand, relevant customer questions, and the decisions the business wants to influence.

Synthetic prompts are still synthetic. They do not become real prompt-volume data simply because they are well researched, but they give us a much stronger test set.

 

3. Run every prompt multiple times

AI answers are not consistent. That’s a feature, not a bug. The inherent variability, some level of randomness, is what makes large language models work.

A brand may appear in one response and disappear from the next. Its position may change and the cited sources may be different. Even the description of the same product or company can vary.

Because of this, running each prompt once is not enough. If we run a prompt once, we record an answer. But if we run it multiple times, we start measuring a pattern. All our measurements are about probability, not certainty.

Repeated runs reveal how much brand visibility can vary between AI responses. A larger sample helps separate recurring patterns from one-off results.

 

For our monitoring setup, we run every prompt several times on each selected AI model. We use Rankscale to manage the synthetic prompt tracking and compare brand performance across repeated runs and monitoring periods.

A larger sample helps us distinguish a recurring result from a one-off answer.

Across those runs, we can measure:

Collecting more answers does not eliminate all AI variability, but it makes the results more statistically accurate and directionally useful.

 

4. Choose which AI platforms to monitor

Tracking every available AI model is not necessarily the best starting point.

We first look at the platforms that already interact with the website or send measurable traffic.

Google Analytics can show AI referral sources such as ChatGPT, Gemini, or Perplexity when users reach the website through links in AI-generated answers.

AI referral traffic in Google Analytics helps identify which platforms are already sending users to the website

 

Server logs provide another layer. They can reveal known AI user agents and show which AI crawlers are accessing the website.

These datasets answer different questions:

But neither provides a complete picture on its own. Together they help us prioritize the platforms that are currently most relevant to the business.

The tracked model set can then expand as usage patterns, target markets, or platform relevance change.

 

5. Put AI visibility into business context

AI visibility is only one signal.

A higher mention rate looks good in a dashboard, but it does not automatically mean stronger brand growth or better business performance.

We also need to ask:

Correlation does not automatically prove that AI visibility caused the result. But without connecting visibility data to other performance signals, we are left with another isolated metric.

The purpose of monitoring is not just to show that the brand appeared in an AI answer. We need to understand where it appears, how it compares to competitors, what information shapes the answer, and whether changes in visibility correspond with meaningful business outcomes.

Analysis leads to action

AI visibility monitoring is a sampling problem first and an interpretation problem second.

A useful setup starts with existing demand data and real customer questions. It turns them into realistic prompts, runs those prompts enough times to identify consistent patterns, and monitors the AI platforms that matter to the business.

The results then need to be read alongside traffic, engagement, conversions, and profit. This is also how we approach SEO today, as a broader visibility system that connects traditional search, AI-driven discovery, and business performance.

If you are setting up AI visibility monitoring and want to compare your current approach with ours, or if you’re just getting ready to start your AI visibility optimization – get in touch by filling out the form below. We can help you build a prompt set and monitoring workflow grounded in your market, customer questions, and business priorities.

Looking for more practical SEO workflows? Explore our SEO insights 👇

Since Google Analytics switched from Universal to GA4, the page loading speed monitoring was no longer available in GA by default. So, we had to reproduce that functionality with Google Tag Manager using custom JavaScript. There are many code examples for achieving this for regular websites, but we haven’t found any that work with single-page application websites (SPA).

Long story short – after much vibe coding and flesh-n-blood developer verification, here’s the full JS code that works for both SPA and regular websites. You can just paste it into your GTM as a Custom HTML tag and trigger on Initialization – All Pages. The code pushes one event into the dataLayer with just one value, the calculated page loading time in seconds. It works for each page refresh, navigation, or URL history change.

We could get deep into a discussion about whether this tracks “true” page loading time, and we’re open to that discussion – that’s what the comment section is for. But as with anything else in web analytics, we believe this method is good enough to be directionally useful. We haven’t encountered any issues so far, so please let us know if you see any strange behavior.

Here’s the full code.

Code example
<script>
(function () {
  // Prevent double setup
  if (window.__pageLoadTimingSetupDone) return;
  window.__pageLoadTimingSetupDone = true;

  var dataLayer = window.dataLayer = window.dataLayer || [];
  var perf = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance;
<script>
(function () {
  // Prevent double setup
  if (window.__pageLoadTimingSetupDone) return;
  window.__pageLoadTimingSetupDone = true;

  var dataLayer = window.dataLayer = window.dataLayer || [];
  var perf = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance;

  function toSecondsTwoDecimals(ms) {
    // ms -> seconds with 2 decimals
    return Math.round(ms / 10) / 100;
  }

  function pushTiming(ms, source) {
    if (!ms || ms <= 0) return;

    dataLayer.push({
      event: 'page_load_time_calc',
      page_load_timing: toSecondsTwoDecimals(ms),
      page_load_source: source
    });
  }

  /* -------------------------------
   *  INITIAL HARD PAGE LOAD
   * ----------------------------- */

  var initialSent = false;

  function calcInitialLoad() {
    if (initialSent || !perf) return;

    var ms = 0;

    // Navigation Timing v2
    if (perf.getEntriesByType && typeof perf.getEntriesByType === 'function') {
      var navEntries = perf.getEntriesByType('navigation');
      if (navEntries && navEntries.length) {
        var nav = navEntries[0];

        if (typeof nav.loadEventEnd === 'number' && nav.loadEventEnd > 0) {
          ms = nav.loadEventEnd; // since timeOrigin
        } else if (typeof nav.domComplete === 'number' && nav.domComplete > 0) {
          ms = nav.domComplete;
        }
      }
    }

    // Legacy Navigation Timing fallback
    if (!ms && perf.timing) {
      var t = perf.timing;
      if (t.loadEventEnd && t.navigationStart) {
        ms = t.loadEventEnd - t.navigationStart;
      } else if (t.domComplete && t.navigationStart) {
        ms = t.domComplete - t.navigationStart;
      }
    }

    if (ms && ms > 0) {
      initialSent = true;
      pushTiming(ms, 'initial');
    }
  }

  if (document.readyState === 'complete') {
    // Load already fired
    calcInitialLoad();
  } else {
    window.addEventListener('load', calcInitialLoad);
  }

  /* -------------------------------
   *  SPA NAVIGATION TIMING
   * ----------------------------- */

  if (!perf) return; // need perf for SPA timing

  var spaStart = null;
  var spaTimer = null;

  function nowMs() {
    if (perf.now && typeof perf.now === 'function') {
      return perf.now(); // relative to timeOrigin
    }
    return Date.now(); // fallback
  }

  function startSpaNav() {
    spaStart = nowMs();

    if (spaTimer) {
      clearTimeout(spaTimer);
      spaTimer = null;
    }

    // Heuristic: SPA "loaded" 1500ms after navigation
    spaTimer = setTimeout(function () {
      endSpaNav('spa_timeout');
    }, 1500);
  }

  function endSpaNav(reason) {
    if (spaStart == null) return;

    var duration = nowMs() - spaStart;
    spaStart = null;

    if (spaTimer) {
      clearTimeout(spaTimer);
      spaTimer = null;
    }

    if (duration > 0) {
      pushTiming(duration, 'spa');
    }
  }

  // Patch history API once to detect SPA route changes
  try {
    var originalPushState = history.pushState;
    history.pushState = function () {
      var rv = originalPushState && originalPushState.apply(this, arguments);
      startSpaNav();
      return rv;
    };

    var originalReplaceState = history.replaceState;
    history.replaceState = function () {
      var rv = originalReplaceState && originalReplaceState.apply(this, arguments);
      startSpaNav();
      return rv;
    };

    window.addEventListener('popstate', function () {
      startSpaNav();
    });
  } catch (e) {
    // fail silently if history patching isn't allowed
  }
})();
</script>

As a side note, this has nothing to do with the Web Vitals measurement protocol. You can implement (Core) Web Vitals independently as a new tag in GTM through a well-maintained tag template by Simo Ahava. However, be aware that there are some limitations with CWV measurement on SPA websites.

Any SEO expert that ever crawled a large website has faced this dilemma. When the audit is done and you’re left with a huge table of broken internal links – how should you present that to the client?

Most SEOs do one of the two:

The first method relies heavily on the client’s team to make sense of the audit and the massive table of inlinks, while the second approach may lead to faster but incomplete resolution.

While both are standard practices among SEO consultants, we have never been truly comfortable with either. So we went a step or two further to make our technical SEO audits more readable and actionable, ultimately leading to greater issue resolution, easier workflow for all teams involved, and generally better client relationships.

Here’s our process with one massive e-commerce site as an example:

It all starts with a Screaming Frog SEO spider crawl

For this site, the crawl ran for about 35 hours. Among other issues and exports, we pulled out a CSV of all link relationships on the website. That CSV was about 20GB large, with 50 million rows (yikes!). We wouldn’t want to burden the client’s team with downloading and unpacking such a huge table; those folks should be busy fixing the website, not figuring out how to work with massive CSVs.

Removing the burden of handling a massive dataset

So instead, we uploaded the file with all link relationships to Google Cloud Storage and then into BigQuery. The BQ file size limit is 100MB, so our 20GB file had to go to Storage first.

One important note when loading the default Screaming Frog export CSV into a BigQuery table – it won’t work with the default Create table settings; you’ll get an error message. You have to make these 4 changes under Advanced options:

We did some minor transformations in BigQuery, just cleaning a few fields and removing ones that aren’t needed, saving the output as a BQ View table.

The Dashboard

Finally, we built a quick but comprehensive Data Studio dashboard to not just visualize the data, but as a functional tool for the client’s web team to work with. They could easily select all inlinks with specific issues, like 5xx and 4xx errors, redirects, empty category pages, or discontinued products.

Status code filter

We even went a step further and prepared a companion document with pre-filtered links to the dashboard containing complex RegEx filters. For example, URL paths containing the double slash (https?://.*//.*), any non-ASCII characters (.*[^\x00-\x7F].*), or diacritical characters specifically (.*[ČčĆćĐ𩹮ž].*), and even repeated folder paths – that needed to be manually generated with their specific folders because of the RE2 RegEx limitations.
Just remember to enable “view filters in report link” in the Data Studio report settings.

The dashboard workflow is simple, yet it reveals all instances of a particular issue found in the crawl.

First, use the filters to focus on a specific linking error → the first table lists all the link destination URLs that match selected filters.

Filter the issue → see every affected destination URL

Then click any of those rows and see the table below which reveals all the pages linking to that URL, and exactly in which on-page elements is the link located. Also, the anchor text, and whether the link is found in the initial HTML, JS-rendered page, or both.

Click a destination URL → get the exact source pages, placement, and anchor text that need fixing

Finally, since this table also contains internal links to all assets, not just web pages, the same dashboard can be used to find oversized images, ZIP files or PDFs on the website. Handy isn’t it?

Why this matters

We hope this inspires other SEOs to put less pressure on their client’s internal teams and make it as easy as possible for them to work with the SEO audit findings. Technical SEO is difficult enough; a consultant’s job is not to give the client a headache, but to reduce friction and drive action leading to meaningful improvements.

Learn more about our approach to SEO and discover how we help businesses stand out online.