# AI Brand Visibility Tracker: ChatGPT, Gemini & Perplexity (GEO) (`sefran1/ai-brand-visibility-checker`) Actor

Check how often ChatGPT (OpenAI web search), Gemini and Perplexity mention your brand vs competitors for real buyer prompts. Mention rate over repeated runs, cited URLs, share of voice, sources AI cites. Bring your own API key. $0.04 per prompt x engine.

- **URL**: https://apify.com/sefran1/ai-brand-visibility-checker.md
- **Developed by:** [Iberia Channel Desk](https://apify.com/sefran1) (community)
- **Categories:** SEO tools, AI, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $40.00 / 1,000 prompt x engine 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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## AI Brand Visibility Tracker: ChatGPT, Gemini & Perplexity (GEO / AEO)

Find out **how often AI assistants recommend your brand** when buyers ask questions, compared with your competitors, and **which sources the AI cites**. Each prompt is asked several times per engine, because AI answers change on every run. You get a **mention rate with a confidence interval** instead of a fake "AI rank".

**Why bring your own key?** You pay the AI provider directly at cost (typically a fraction of a cent per answer) instead of a bundled markup, and your prompts go only to the provider you choose. Fastest start: a **free Google AI Studio key** (aistudio.google.com/apikey, about a minute, no card) runs the Gemini engine. Add OpenAI or Perplexity keys for live web-search answers.

**First, check AI bots can read your site at all.** If ChatGPT's or Perplexity's crawler is blocked by robots.txt or your CDN, they can't cite you no matter what you publish. Run [AI Crawler Access Audit](https://apify.com/sefran1/ai-crawler-access-audit) (no key, $0.02 per page) before tracking mentions.

### Sample output

One record per prompt × engine, plus one summary record. This is real output, recorded on 2026-10-17 with Gemini (free key, so no web search). It's the same sample the Actor returns in demo mode when no API key is given.

```json
{
  "type": "check",
  "prompt": "What is the best note-taking app for a small team?",
  "engine": "gemini",
  "model": "gemini-3.1-flash-lite",
  "webSearch": false,
  "runsRequested": 2,
  "runsSucceeded": 2,
  "brand": "Notion",
  "brandMentioned": 2,
  "mentionRate": 1,
  "mentionRate95": {
    "low": 0.342,
    "high": 1
  },
  "avgRankWhenMentioned": 1,
  "mentionSnippets": [
    "op four recommendations based on how your team operates: --- ### 1. Notion: The \"All-in-One\" Workspace **Best for:** Teams that want to combine"
  ],
  "domainCitedRate": 0,
  "citedUrls": [],
  "topCitedDomains": [],
  "competitors": [
    {
      "name": "Obsidian",
      "mentions": 2,
      "mentionRate": 1,
      "avgRank": 2,
      "citedRate": 0
    },
    {
      "name": "Evernote",
      "mentions": 0,
      "mentionRate": 0,
      "avgRank": null,
      "citedRate": 0
    },
    {
      "name": "Coda",
      "mentions": 2,
      "mentionRate": 1,
      "avgRank": 3,
      "citedRate": 0
    }
  ],
  "shareOfVoice": {
    "Notion": 0.333,
    "Obsidian": 0.333,
    "Evernote": 0,
    "Coda": 0.333
  },
  "sampleAnswer": "There is no single \"best\" app, because the right choice depends on your team’s **workflow culture**. Do you need a structured database, a free-flowing collaborative canvas, or a project management hub?\n\nHere are the top …",
  "checkedAt": "2026-10-17T18:46:27.161Z"
}
```

Summary record:

```json
{
  "type": "summary",
  "brand": "Notion",
  "domain": "notion.so",
  "prompts": 2,
  "checks": 2,
  "checksWithResults": 2,
  "answersAnalyzed": 4,
  "mentionRate": 1,
  "mentionRate95": {
    "low": 0.51,
    "high": 1
  },
  "domainCitedRate": 0.25,
  "engines": [
    {
      "engine": "gemini",
      "model": "gemini-3.1-flash-lite",
      "webSearch": false,
      "checks": 2,
      "runs": 4,
      "mentionRate": 1,
      "mentionRate95": {
        "low": 0.51,
        "high": 1
      },
      "domainCitedRate": 0.25
    }
  ],
  "shareOfVoice": {
    "Notion": 0.333,
    "Obsidian": 0.333,
    "Evernote": 0.167,
    "Coda": 0.167
  },
  "topCitedDomains": [
    {
      "domain": "getupnote.com",
      "count": 1
    },
    {
      "domain": "onenote.com",
      "count": 1
    },
    {
      "domain": "obsidian.md",
      "count": 1
    },
    {
      "domain": "logseq.com",
      "count": 1
    }
  ],
  "promptsWhereBrandIsAbsent": [],
  "howToRead": "(explains each metric)",
  "errors": [],
  "checkedAt": "2026-10-17T18:46:42.703Z"
}
```

### What it measures

| Field | Meaning |
|---|---|
| `mentionRate`, `mentionRate95` | Share of answers that name your brand (whole-word match, plus aliases), with a 95% Wilson interval |
| `avgRankWhenMentioned` | Order of first mention among your brand and the tracked competitors. Noisy, so read it as a hint only |
| `mentionSnippets` | The text around each match, so you can audit it |
| `domainCitedRate`, `citedUrls`, `topCitedDomains` | How often your domain is cited or linked, and which sites the AI used as sources |
| `competitors`, `shareOfVoice` | Mention rate per competitor, and each brand's share of all tracked mentions |
| `webSearch` | `true` when the engine searched the web (ChatGPT search, Google Search grounding, Perplexity), `false` when it answered from model memory |
| `promptsWhereBrandIsAbsent` (summary) | The prompts where you are missing and which competitors show up instead |

### How to use

1. Enter your **brand**, **domain** and 2–5 **competitors**.
2. Add the **buyer prompts** your customers would type, e.g. "best price tracker for Wallapop", or just describe what you sell and let the Actor generate prompts in your language.
3. Paste **at least one API key** (Gemini, OpenAI or Perplexity) in the *API keys (bring your own)* section. Keys are encrypted secret inputs, sent only to that provider, and never written to the output.
4. Set **runs per prompt** (3 is a good start, 5–10 for tighter intervals) and run. Schedule it weekly to track the trend.

Without any key, the Actor returns the clearly labelled **demo sample** (`"demo": true`, not charged) for the sample brand. For any other brand it stops with a message explaining how to add a key.

### Engines

- **OpenAI**: Responses API with the `web_search` tool (default model `gpt-4.1-mini`, the closest to ChatGPT Search). Optional `country` sets the search location.
- **Google Gemini**: Google Search grounding (default `gemini-flash-latest`). Note that free AI Studio keys usually can't use grounding on current models. The Actor then falls back to a no-search answer and marks `webSearch: false`. Turn this off with `allowUngroundedFallback`.
- **Perplexity**: `sonar` with its citations and search results.

### Pricing

**$0.04 per prompt × engine check**, and all repeated runs of that prompt on that engine are included. You pay the AI provider for the calls with your own key, usually fractions of a cent each. Failed checks (bad key, provider errors) and demo output are **not** charged. Example: 10 prompts × 3 engines × 3 runs = 30 checks = $1.20 on Apify.

### Tips

- Visibility is a **rate**, not a rank. SparkToro's 2026 study found AI tools almost never return the same list twice, so measure many prompts, several runs each.
- Add your city or country to some prompts if you sell locally. Local prompts often surface smaller brands.
- `topCitedDomains` shows where AI looks. Being mentioned on those sites (reviews, directories, comparisons, forums) usually moves visibility more than on-page tweaks.
- If your brand is also a common word, turn on **case-sensitive match** and check `mentionSnippets`.

### Limits

API answers can differ from the consumer apps (personalization, ads, model versions), so treat results as a consistent benchmark, not a screenshot of what every user sees. There are at most 50 prompts and 10 runs per prompt per run.

# Actor input Schema

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

The name to look for in AI answers (case-insensitive, whole words). Leave the sample "Notion" and run without keys to see a recorded demo.

## `domain` (type: `string`):

Used to detect when AI engines cite your site, e.g. notion.so or apify.com/yourname/your-actor (a path limits matching to that section).

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

Other spellings that count as a mention (e.g. product names, old names).

## `caseSensitive` (type: `boolean`):

Turn on when your brand is also a common word (e.g. "Regateo" vs "regateo" in Spanish). Check mentionSnippets to audit matches.

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

Competitor names to compare share of voice against.

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

Questions your buyers ask AI assistants. Up to 50. Leave empty and fill "description" to auto-generate them.

## `description` (type: `string`):

Used only when "prompts" is empty: one or two sentences about your product, e.g. "Price-history browser extension for second-hand marketplaces in Spain".

## `numPrompts` (type: `integer`):

Only used with auto-generation.

## `language` (type: `string`):

Language for generated prompts, e.g. en, es, fr, de, pt, it.

## `country` (type: `string`):

Optional two-letter country, e.g. ES or GB. Sent to OpenAI web search as the user location and used for generated prompts.

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

Leave empty to use every engine you gave a key for.

## `runsPerPrompt` (type: `integer`):

AI answers change on every run, so each prompt is asked several times and the mention rate is the share of runs that name you. All runs of one prompt on one engine are one billable check.

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

From aistudio.google.com/apikey. Free-tier keys usually cannot use Google Search grounding on current models; the Actor then falls back to answers without web search and marks them webSearch=false.

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

From platform.openai.com/api-keys. Uses the Responses API web_search tool.

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

From perplexity.ai/settings/api.

## `geminiModel` (type: `string`):

Default gemini-flash-latest.

## `openaiModel` (type: `string`):

Default gpt-4.1-mini (supports web search).

## `perplexityModel` (type: `string`):

Default sonar.

## `webSearch` (type: `boolean`):

Ask engines to search the web like ChatGPT Search / AI Overviews. Off = model memory only.

## `allowUngroundedFallback` (type: `boolean`):

If Gemini web-search grounding is not available for your key (free tier), answer without web search instead of failing; records are marked webSearch=false.

## `includeFullAnswers` (type: `boolean`):

Adds all raw answers and citations to each record (larger dataset).

## `maxConcurrency` (type: `integer`):

How many prompt x engine checks run in parallel. Lower it if your provider rate-limits you.

## `demo` (type: `boolean`):

Return the recorded sample instead of calling any AI engine (not charged).

## Actor input object example

```json
{
  "brand": "Notion",
  "domain": "notion.so",
  "brandAliases": [],
  "caseSensitive": false,
  "competitors": [
    "Obsidian",
    "Evernote",
    "Coda"
  ],
  "prompts": [
    "What is the best note-taking app for a small team?",
    "What are good alternatives to Evernote?"
  ],
  "numPrompts": 5,
  "language": "en",
  "runsPerPrompt": 3,
  "webSearch": true,
  "allowUngroundedFallback": true,
  "includeFullAnswers": false,
  "maxConcurrency": 4,
  "demo": false
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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": "Notion",
    "domain": "notion.so",
    "brandAliases": [],
    "competitors": [
        "Obsidian",
        "Evernote",
        "Coda"
    ],
    "prompts": [
        "What is the best note-taking app for a small team?",
        "What are good alternatives to Evernote?"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sefran1/ai-brand-visibility-checker").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": "Notion",
    "domain": "notion.so",
    "brandAliases": [],
    "competitors": [
        "Obsidian",
        "Evernote",
        "Coda",
    ],
    "prompts": [
        "What is the best note-taking app for a small team?",
        "What are good alternatives to Evernote?",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("sefran1/ai-brand-visibility-checker").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": "Notion",
  "domain": "notion.so",
  "brandAliases": [],
  "competitors": [
    "Obsidian",
    "Evernote",
    "Coda"
  ],
  "prompts": [
    "What is the best note-taking app for a small team?",
    "What are good alternatives to Evernote?"
  ]
}' |
apify call sefran1/ai-brand-visibility-checker --silent --output-dataset

```

## MCP server setup

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

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/tfSB2B5QIwCeJ4Q6Y/builds/u9zOgbJ1Lb3L0hvwU/openapi.json
