A competitor monitor can answer a narrow, useful question: which domains were cited in Google AI Overviews for the searches we checked? It cannot tell you why Google selected them, and it should not turn a small keyword sample into a market-share claim.

Build the monitor around repeatable observations. Fix the query set, country, language, and collection schedule. Store every returned source and the answer text, then calculate metrics only from successful, eligible checks.

What should an AI Overview competitor monitor measure?

Measure citation presence by query, cited source slots by domain, and changes between observation periods. Keep direct mentions in ai_overview in a separate column. A company name in the answer is not the same event as a link to that company's website.

The Google AI Overviews API response tested on August 5, 2026 contained an ordered sources array. Each source included a URL, title, site, and position. That gives the monitor a defensible unit: one returned source record in one dated observation.

Inspect the source records for one query

Use one observation model for every domain

Do not create one table per competitor. Store results without deciding in advance which domains matter. A new source may appear later, and the raw observation should still be usable.

CREATE TABLE overview_run (
  id             BIGINT PRIMARY KEY,
  query_text     TEXT NOT NULL,
  country_code   CHAR(2) NOT NULL,
  language_code  VARCHAR(10) NOT NULL,
  observed_at    TIMESTAMP NOT NULL,
  request_state  VARCHAR(30) NOT NULL,
  answer_text    TEXT NULL
);

CREATE TABLE overview_citation (
  run_id          BIGINT NOT NULL,
  source_position INTEGER NULL,
  source_domain   TEXT NOT NULL,
  source_url      TEXT NOT NULL,
  source_title    TEXT NULL
);

Add a query topic or campaign ID in your application model. That lets an analyst compare product questions with informational questions without pretending that they carry the same commercial weight.

Collect and normalize the returned citations

function normalizeDomain(source) {
  const value = source.site || new URL(source.url).hostname;
  return value.toLowerCase().replace(/^www\./, "").replace(/\.$/, "");
}

function citationRows(runId, payload) {
  const sources = Array.isArray(payload?.data?.sources)
    ? payload.data.sources
    : [];

  return sources
    .filter((source) => source && typeof source.url === "string")
    .map((source) => ({
      runId,
      position: Number.isInteger(source.position) ? source.position : null,
      domain: normalizeDomain(source),
      url: source.url,
      title: source.title ?? null
    }));
}

Keep the URL as returned. Domain normalization is for grouping, not for erasing evidence. If two URLs redirect to one canonical page, enrich that in a separate job.

Log HTTP errors as failed runs. We saw temporary HTTP 500 responses during the live test. Counting those as zero citations would make every domain look weaker when the collector was the part that failed.

Build the collector from a live response

Competitor metrics that stay interpretable

Three definitions are enough for a first report, but they should not be forced into one score.

Metric Formula What it says
Query citation rate Queries citing domain / eligible successful queries How often the domain appeared at least once
Source-slot share Domain source rows / all source rows How much of the returned citation list it occupied
Median source position Median returned position for domain Where its citations appeared in the source array

An eligible query is one whose request succeeded and returned the kind of source data required by the metric. Publish that denominator beside the result. A percentage without its query count is easy to overread.

SELECT
  source_domain,
  COUNT(DISTINCT run_id) AS cited_runs,
  COUNT(*) AS source_slots,
  MIN(source_position) AS best_observed_position
FROM overview_citation
WHERE run_id IN (
  SELECT id FROM overview_run
  WHERE request_state = 'success'
    AND observed_at >= :period_start
    AND observed_at < :period_end
)
GROUP BY source_domain
ORDER BY cited_runs DESC, source_slots DESC;

Compare matched periods, not screenshots

A screenshot can verify one result. It cannot show a trend. Compare two periods built from the same query IDs and market settings. If the query list changed, label the comparison as unmatched or calculate only on the intersection.

Change Evidence to keep
Newly cited domain First matching source URL and observation
Lost citation Previous match plus current successful non-match
Different cited page Old and new URLs for the same domain
Answer mention changed Matched answer snapshots, separate from citations

Use the single-domain citation checker when the job is a quick audit. Read the ranking and citation comparison before mixing classic rank positions into this report.

What this monitor cannot prove

The data shows what the API observed for the supplied inputs. It does not prove why a page was cited, whether every user saw the same result, or whether a content change caused the next citation change. Google says AI features can use related searches across subtopics and that the responses and links can vary.

Keep editorial explanations in a notes field, separate from collected facts. If an analyst believes a competitor page answers a missing subtopic, label that as a hypothesis to review, not as an API finding.

Sources checked August 5, 2026

Start a dated competitor citation baseline