# AI Visibility Tracker: GEO/AEO Brand Monitor with Trends (`reminiscent_folder/ai-visibility-trend-tracker`) Actor

Track how often ChatGPT, Google AI Overviews, Perplexity and Gemini mention your brand — with confidence intervals on every number and a tested month-over-month comparison, so you only act on changes that are real.

- **URL**: https://apify.com/reminiscent\_folder/ai-visibility-trend-tracker.md
- **Developed by:** [Paul Bernabeu](https://apify.com/reminiscent_folder) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $60.00 / 1,000 ai visibility checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## AI Visibility Tracker — GEO/AEO monitor with month-over-month trends

**Track how often ChatGPT, Google AI Overviews, Perplexity and Gemini mention your brand — with a confidence interval on every number, and a statistically tested comparison against your last run.**

Most AI-visibility tools hand you a single percentage from a single pass. That number is noise. Ask an AI assistant the same question twice and you get different answers: [SparkToro's study of 2,961 prompt executions](https://sparktoro.com/blog/new-research-ais-are-highly-inconsistent-when-recommending-brands-or-products-marketers-should-take-care-when-tracking-ai-visibility/) found under a 1-in-100 chance that two runs return the same brand list. Report "share of voice: 34%" to a client, get 22% next week, and you have lost the client.

This Actor is built around that problem.

***

### What makes it different

**1. Every number carries a confidence interval.**
Each prompt is asked *k* times per engine (default 3). Results are reported as a Wilson score interval, which stays correct at small sample sizes and at rates of 0% or 100% — where the usual approximation produces intervals of zero width or bounds outside 0–100%. The interval, not the headline figure, is the result.

**2. It remembers. Every other tracker forgets.**
Runs are stored under a `projectId` in a named key-value store that survives between runs. Run it again next month on the same `projectId` and you get a real comparison. This is the difference between a one-off audit and something worth scheduling.

**3. It refuses to report noise as a trend.**
Month-over-month movements go through a two-proportion z-test. Only movements outside the noise band are reported as changes. Everything else is returned in `suppressedAsNoise` — visible, but not presented as a finding.

> A 50% → 60% move at n=20 is noise. The *same* 10-point move at n=600 is real. Almost every tool in this category reports both as a trend.

**4. It separates grounded from parametric visibility.**
An answer produced after a live web search responds to content and PR work this quarter. An answer produced from model weights does not. They are different metrics with different remedies, and collapsing them into one score hides which lever applies. Both are reported separately.

**5. "No answer" and "collection failed" are never conflated.**
Google decides whether an AI Overview appears; when none appears that is a real observation. A blocked fetch is missing data. Mixing them silently depresses your score. They are counted separately, and rates are computed over successful samples only.

**6. It produces something you can hand to a client.**
A self-contained, printable HTML report is saved under the key `REPORT`, alongside the raw dataset.

***

### What it costs

**What this Actor charges:**

| Event | Price | When |
|---|---|---|
| `actor-start` | $0.01 | once per run |
| `visibility-check` | $0.035 | per prompt × engine × sample |
| `trend-analysis` | $1.00 | only when a previous run exists to compare against |
| `agency-report` | $2.00 | only when the HTML report is generated |

**You are only charged for checks that actually return an answer.** Google frequently shows no AI Overview for a given query. Those samples are recorded as real observations, and they are not billed.

**Plus downstream cost, which you should read before running at volume.** The scraped engines run through Apify's own Actors and bill your Apify account separately:

| Engine | Free plan | Bronze and above |
|---|---|---|
| Google AI Overview | $0.003/query | $0.002 or less |
| Google AI Mode | **$0.20/query** | $0.005 or less |
| ChatGPT search | **$0.20/query** | $0.005 or less |

On the free plan AI Mode and ChatGPT cost roughly **66× more** than AI Overview, so **only Google AI Overview is enabled by default**. Turn the others on deliberately, and preferably on a paid plan.

**Worked example, 20 prompts × 3 samples on Google AI Overview:** 60 checks = $2.11 from this Actor, plus $1.00 trend and $2.00 report, plus about $0.18 downstream. **Roughly $5.30 for a monthly audit.**

The full plan, including downstream estimate, is printed in the log **before** any work starts, and the run stops cleanly at your `maxTotalChargeUsd` rather than overrunning it.

Compare: Profound starts at $99/month, Scrunch at $300, Otterly at $29 for 15 prompts. Those are subscriptions, and all of them gate API and raw-data access behind an enterprise tier.

> **Try it free.** Set `testMode: true` to generate deterministic synthetic answers. Nothing is charged, no engine is contacted, and you see the exact output schema and report layout before spending anything.

***

### How the data is collected — disclosed per engine

Vendors in this category are routinely vague about this. Here it is in full.

| Engine | Method | Key needed | Billed by |
|---|---|---|---|
| Google AI Overview | `apify/google-ai-overviews-scraper` | no | your Apify account (downstream) |
| Google AI Mode | `apify/google-ai-mode-scraper` | no | your Apify account (downstream) |
| ChatGPT (search) | `apify/chatgpt-search-scraper` | no | your Apify account (downstream) |
| Perplexity | official Sonar API | yes | Perplexity |
| Gemini | Gemini API + Google Search grounding | yes | Google |
| Claude | Anthropic API + `web_search` (`max_uses: 1`) | yes | Anthropic |
| OpenAI | Responses API + `web_search` | yes | OpenAI |

The three scraped surfaces run through **Apify's own first-party Actors**. That is deliberate: proxies, browser fingerprinting, captchas and HTML parsing are the parts that rot fastest, and Apify maintains them daily. Their platform usage is billed to your Apify account separately and is called out in the run log.

The API-based engines use **your own keys** and are billed to you by the provider. Nothing is resold.

Three details that matter, all found by running the upstream Actors rather than reading their docs:

- **Google's citation links cannot be resolved.** AI Overview returns session-scoped stubs like `/goto?url=CAESmwEB...`, which return HTTP 400 when fetched independently. So for the Google engines this Actor reports the **publisher name read from the citation title** (`reddit.com/r/devops`, `Chaser`, `Fluid CRM Blog`) rather than pretending to have a URL. Engines you supply a key for return real URLs, and those are reported as resolved domains.
- **The scraped Google surfaces have no locale control.** The upstream Actors accept only a list of queries: no country, no language. Results come from wherever the proxy exits, which in testing was not Europe. If you need a specific market, use the API-based engines with your own key.
- Gemini's grounding URLs are `vertexaisearch` redirects that **expire**, so they are resolved at collection time and anything unresolvable is dropped rather than stored. Claude's `web_search` is pinned to `max_uses: 1` so one request cannot silently trigger several billable searches.

**On efficiency:** each sample round sends every prompt to an engine in a single upstream run, so a 20-prompt, 3-sample job is 3 upstream runs rather than 60. The upstream Actors deduplicate identical queries, which is why repeated samples cannot share a run.

***

### Input

```json
{
  "brand": "Linear",
  "brandAliases": ["Linear.app"],
  "competitors": ["Jira", "Asana", "Notion", "ClickUp"],
  "ownedDomains": ["linear.app"],
  "prompts": [
    "best project management tool for a small design agency",
    "what should I use instead of Jira",
    "affordable issue tracker for a startup engineering team"
  ],
  "engines": ["google_ai_overview", "chatgpt_search"],
  "samplesPerPrompt": 3,
  "projectId": "linear",
  "compareWithPrevious": true,
  "generateHtmlReport": true
}
```

Write prompts the way a customer would ask them. `"best project management tool for a small design agency"` measures something. `"project management"` does not.

Keep `projectId` stable across runs — it is what makes the comparison possible.

***

### Output

Three record types in the dataset, plus `SCORECARD`, `TREND` and `REPORT` in the key-value store.

**Scorecard** — overall rates with intervals, per-engine and per-prompt breakdowns, cited source domains, competitor share, and a `dataQuality` block that says plainly when there is not enough data to make a claim.

**Sample** — one row per prompt × engine × repetition, so any headline number can be audited back to the raw observations behind it:

```json
{
  "type": "sample",
  "prompt": "best project management tool for a small design agency",
  "engine": "chatgpt_search",
  "sampleIndex": 2,
  "status": "ok",
  "mode": "grounded",
  "brandMentioned": true,
  "mentionRank": 2,
  "recommended": true,
  "sentiment": "positive",
  "competitorsMentioned": ["Asana", "Notion"],
  "citedDomains": ["g2.com", "reddit.com"],
  "ownedDomainCited": false
}
```

**Trend** — only present from the second run onward:

```json
{
  "type": "trend",
  "daysBetween": 31,
  "changes": [
    {
      "metric": "mentionRate", "scope": "overall",
      "previous": 0.364, "current": 0.625, "delta": 0.261,
      "significant": true, "pValue": 0.0002, "direction": "up",
      "interpretation": "Increase of +26.1 pts (p=0.0002). This is outside the noise band and worth acting on."
    }
  ],
  "suppressedAsNoise": [ "…movements that did not clear the test" ],
  "newCitedDomains": ["theverge.com"],
  "lostCitedDomains": []
}
```

***

### The most useful field

The cited sources. They tell you what the engines actually drew on, and where a competitor is cited and you are not, that is your content brief. Mention rate tells you the score; cited sources tell you what to do about it.

Read as `topCitedDomains` (resolved domains, from engines returning real URLs) or `topCitedSources` (publisher names, from the Google surfaces, for the reason given above). The report shows whichever is available and labels which it is.

***

### Scheduling

Use Apify's built-in Scheduler and run it weekly or monthly on the same `projectId`. From the second run onward every report opens with what changed and whether the change is real. That is the whole point of the tool.

***

### Method notes

Brand detection, endorsement and sentiment are **deterministic and rule-based**, not LLM calls. Running the analysis twice over the same answers always produces the same numbers. In a tool whose entire claim is reproducibility, the analysis layer must not itself be a source of variance. All sampling variation comes from the engines, where it belongs.

Brand matching is accent- and case-insensitive, respects Unicode word boundaries, and handles possessives and names containing dots and hyphens — so `Apple` matches `Apple's` but not `Applesauce`, and `Node.js` and `Coca-Cola` match correctly.

**On "rank".** Average position across samples is reported with a standard deviation, and should be read only in aggregate. Any tool presenting a single stable "your rank in ChatGPT" is overstating what the underlying data supports.

***

### Limitations, stated plainly

- Google decides whether an AI Overview appears at all. For some queries and countries there simply is not one, and that is recorded as `no_answer`.
- Scraped surfaces reflect a logged-out session, which can differ from a personalised one.
- API-based engines measure that provider's API retrieval, which is not identical to its consumer product.
- Sentiment is lexicon-based. It is reproducible and auditable, but less nuanced than a human read.
- Google's answer payload arrives with the site's own share and feedback UI appended, sometimes in an unrelated language. That chrome is stripped before analysis, and in testing it was 37 to 43% of the raw payload.
- Sample sizes below roughly 9 usable observations produce intervals too wide to support a claim. The Actor tells you when this happens instead of quietly reporting the number anyway.

# Actor input Schema

## `brand` (type: `string`):

The brand name to look for in AI answers. Matching is accent- and case-insensitive and respects word boundaries, so "Apple" will not match inside "Applesauce".

## `brandAliases` (type: `array`):

Other ways the brand is written — legal name, former name, common misspellings, accented and unaccented forms. Any of these counts as a mention.

## `prompts` (type: `array`):

The questions your buyers actually ask an AI assistant. Write them as a customer would, not as a keyword. Good: "best project management tool for a small design agency". Weak: "project management".

## `competitors` (type: `array`):

Named competitors. Used for share of voice and to show who appears when you do not.

## `ownedDomains` (type: `array`):

Domains you control. Used to measure how often an answer cites you as a source rather than merely naming you — the two are different outcomes.

## `engines` (type: `array`):

Which AI answer engines to query. Google AI Overview needs no API key and costs about $0.003 per query downstream. Google AI Mode and ChatGPT search also need no key but cost $0.20 per query on the Apify FREE plan (far less on paid plans), so they are off by default. The remaining engines use official provider APIs and need your own key, billed by that provider.

## `samplesPerPrompt` (type: `integer`):

How many times to ask each prompt on each engine. AI answers vary run to run, so a single sample cannot separate a real change from noise. 3 is the minimum for a usable confidence interval; 5 narrows it further. Each sample round is one upstream Actor run, and this multiplies cost.

## `projectId` (type: `string`):

Groups runs together over time. Keep it the same across runs and each report will compare against the previous one. Defaults to a slug of the brand name.

## `compareWithPrevious` (type: `boolean`):

Run a two-proportion significance test against the last run for this project and report only movements outside the noise band.

## `generateHtmlReport` (type: `boolean`):

Saves a self-contained, printable report to the key-value store under the key REPORT.

## `includeAnswerText` (type: `boolean`):

Store the complete answer for every sample. Useful for auditing a number back to the raw text it came from; makes the dataset considerably larger.

## `confidenceLevel` (type: `number`):

Confidence level for the intervals. 0.95 is standard.

## `testMode` (type: `boolean`):

Generate deterministic synthetic answers so you can see the exact output schema and report layout without spending anything. Nothing is charged, no engine is contacted, and no downstream Actor is run.

## `perplexityApiKey` (type: `string`):

Optional. Enables the Perplexity engine via the official Sonar API. Billed by Perplexity to you, not by this Actor.

## `geminiApiKey` (type: `string`):

Optional. Enables Gemini with Google Search grounding.

## `anthropicApiKey` (type: `string`):

Optional. Enables Claude with the web\_search tool.

## `openaiApiKey` (type: `string`):

Optional. Enables OpenAI with the web\_search tool.

## Actor input object example

```json
{
  "brand": "Linear",
  "brandAliases": [
    "Linear.app"
  ],
  "prompts": [
    "best project management tool for a small design agency",
    "what should I use instead of Jira",
    "affordable issue tracker for a startup engineering team"
  ],
  "competitors": [
    "Jira",
    "Asana",
    "Monday.com",
    "ClickUp",
    "Notion",
    "Trello"
  ],
  "ownedDomains": [
    "linear.app"
  ],
  "engines": [
    "google_ai_overview"
  ],
  "samplesPerPrompt": 3,
  "projectId": "linear-demo",
  "compareWithPrevious": true,
  "generateHtmlReport": true,
  "includeAnswerText": false,
  "confidenceLevel": 0.95,
  "testMode": false
}
```

# Actor output Schema

## `results` (type: `string`):

Scorecard, individual samples and month-over-month changes. Use the view tabs to switch between them.

## `report` (type: `string`):

Shareable HTML report. Produced when the agency-report event is enabled.

## `scorecard` (type: `string`):

Headline visibility metrics with confidence intervals for this run.

## `trend` (type: `string`):

Movements that passed the significance test, plus the ones rejected as noise.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "brand": "Linear",
    "brandAliases": [
        "Linear.app"
    ],
    "prompts": [
        "best project management tool for a small design agency",
        "what should I use instead of Jira",
        "affordable issue tracker for a startup engineering team"
    ],
    "competitors": [
        "Jira",
        "Asana",
        "Monday.com",
        "ClickUp",
        "Notion",
        "Trello"
    ],
    "ownedDomains": [
        "linear.app"
    ],
    "engines": [
        "google_ai_overview"
    ],
    "projectId": "linear-demo"
};

// Run the Actor and wait for it to finish
const run = await client.actor("reminiscent_folder/ai-visibility-trend-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "brand": "Linear",
    "brandAliases": ["Linear.app"],
    "prompts": [
        "best project management tool for a small design agency",
        "what should I use instead of Jira",
        "affordable issue tracker for a startup engineering team",
    ],
    "competitors": [
        "Jira",
        "Asana",
        "Monday.com",
        "ClickUp",
        "Notion",
        "Trello",
    ],
    "ownedDomains": ["linear.app"],
    "engines": ["google_ai_overview"],
    "projectId": "linear-demo",
}

# Run the Actor and wait for it to finish
run = client.actor("reminiscent_folder/ai-visibility-trend-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "brand": "Linear",
  "brandAliases": [
    "Linear.app"
  ],
  "prompts": [
    "best project management tool for a small design agency",
    "what should I use instead of Jira",
    "affordable issue tracker for a startup engineering team"
  ],
  "competitors": [
    "Jira",
    "Asana",
    "Monday.com",
    "ClickUp",
    "Notion",
    "Trello"
  ],
  "ownedDomains": [
    "linear.app"
  ],
  "engines": [
    "google_ai_overview"
  ],
  "projectId": "linear-demo"
}' |
apify call reminiscent_folder/ai-visibility-trend-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reminiscent_folder/ai-visibility-trend-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/MLU4EV4W7q7t777cS/builds/ZD36HebezvlMRN4gl/openapi.json
