# AI Search Visibility Tracker: Brand in AI (`brightknot/ai-search-visibility-tracker`) Actor

Returns, for each question asked to OpenAI, Claude and Perplexity models, whether your brand was named, its position and list rank, competitors named, share of voice and cited links. Monitor mode shows what changed since the last run. No AI account needed.

- **URL**: https://apify.com/brightknot/ai-search-visibility-tracker.md
- **Developed by:** [Brightknot](https://apify.com/brightknot) (community)
- **Categories:** AI, SEO tools, Marketing
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 72.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.03 / answer checked

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

Find out if AI assistants recommend your brand. Ask OpenAI, Anthropic and Perplexity models the questions your buyers ask, such as "What are the best CRM tools for a small business?", and get one row per answer: was your brand named, in what position, at what rank in the list, which competitors were named before you, your share of voice, and which links were cited.

Turn on **monitor mode** and schedule it weekly: each run also tells you what changed for every question and model since the last run (gained mention, lost mention, moved up, moved down). This is the data behind GEO (generative engine optimization) and AI SEO reporting.

No AI account is needed. Model calls go through Apify's OpenRouter proxy and the model cost is added to your Apify bill.

### For AI agents

Use this Actor when you need to know whether AI assistants recommend a brand for given buyer questions, and which competitors they name instead. Not for web search rankings. It asks AI models, it does not read Google results.

- **Actor id:** `brightknot/ai-search-visibility-tracker`
- **Required input:** `brand`
- **Call:** MCP `call-actor` with the input below, then `get-dataset-items`. Over HTTP: `POST https://api.apify.com/v2/acts/brightknot~ai-search-visibility-tracker/run-sync-get-dataset-items` with the input as the JSON body.
- **Cap the spend** with `maxTotalChargeUsd` in the call options, not in the input.

#### Example input

```json
{
  "brand": "Notion",
  "brandDomains": ["notion.com"],
  "competitors": ["Confluence", "Coda", "ClickUp"],
  "prompts": ["What are the best team wiki apps?", "What is a good alternative to Confluence?"],
  "monitorMode": false
}
```

#### Example output row

Trimmed to the main fields. See the Output schema tab for every field.

```json
{
  "prompt": "What are the best team wiki apps?",
  "model": "~openai/gpt-luna-latest",
  "brand_mentioned": true,
  "brand_position": 1,
  "brand_list_rank": 1,
  "competitors_mentioned": ["Confluence", "Coda", "Obsidian"],
  "share_of_voice": 0.333,
  "cited_urls": [],
  "brand_domain_cited": false
}
```

A `SUMMARY` record has the visibility rate per model and prompt.

#### Cost per call

| Event | Price | When |
|---|---|---|
| Answer checked | USD 0.03 | per answer row returned (failed calls are not charged) |
| Actor start | Apify default | once per run |
| Model usage | under USD 0.001 per answer for Haiku or GPT Luna, about USD 0.006 for Perplexity Sonar (estimates) | billed by Apify's OpenRouter proxy |

The example input is 2 prompts x 3 models = 6 answers: about USD 0.18 plus about USD 0.01 of model cost.

#### Limits

- Answers = prompts x models x repeatsPerPrompt, capped by `maxAnswers` (default 600). Each answer is billed.
- 6 answers take about 30 to 60 seconds. `call-actor` waits at most 45 seconds, so poll larger runs.
- AI answers vary between asks; use repeatsPerPrompt 3 for a steadier rate.
- No API key needed with the default provider. Runs with limited permissions.

### What it does

- Asks each prompt to each model, once or up to 5 times (AI answers vary from one ask to the next, so repeats give a steadier rate).
- Writes prompts for you from your product category, brand and competitors when you do not give your own.
- Finds your brand and its other names as whole words (so "Notion" does not match "notional"), and every competitor you list.
- Reads ranked answers: numbered lists, bullet lists and heading lists. Gives your rank in the list and the competitors' ranks.
- Lists other product names that appear in the answer's lists but are not on your competitor list, so you discover who the AI recommends instead of you.
- Collects cited links (Perplexity citations, OpenRouter link annotations and links in the text) and tells you when one of your own domains is cited.
- Optional: a second small model call rates how the answer describes your brand (positive, neutral or negative) with a one-line reason.
- Saves a run summary with the visibility rate, average position, share of voice and competitor mention rates, overall, per model and per prompt.

### Use cases

- **Marketing and SEO teams:** track your AI visibility each week for 10 to 50 buyer questions and report the trend.
- **Agencies:** one monitor name per client, a scheduled run each Monday, results sent to Google Sheets or Slack with Apify integrations.
- **Product marketing:** see which competitors AI assistants name next to you, and which unknown names keep showing up in their lists.
- **Local businesses and regional brands:** set the market (for example United Arab Emirates or Germany) to see local recommendations.

### Input

| Field | What it does |
|---|---|
| Brand | Your brand or product name. Required. |
| Other names for the brand | Other spellings or product names that count as a mention, e.g. `Notion AI`. |
| Brand websites | Your domains, e.g. `notion.com`. Used to flag answers that cite your site. |
| Competitors | Competitor names to track in the same answers. |
| Product category | Used to write prompts when Prompts is empty, e.g. `project management tools`. |
| Prompts | Your buyers' questions, one per line. |
| AI models | OpenRouter model ids. Default: `~openai/gpt-luna-latest`, `anthropic/claude-haiku-5.5` and `perplexity/sonar` (live web search with citations). |
| Ask each question this many times | 1 to 5. Default 1. |
| Market | Adds "I am based in ..." to every question. |
| Rate how the brand is described | Adds sentiment and a reason when the brand is named. |
| Monitor mode | Compares each answer with the last run of the same monitor. On by default. |
| Monitor name | One history per name. Use one per brand or client. |
| Include the full answer text | On by default. |
| Max answers per run | Safety cap, default 600. |
| Model provider | Apify proxy (default, no key) or your own OpenRouter key. |

Example input:

```json
{
  "brand": "Notion",
  "brandAliases": ["Notion AI"],
  "brandDomains": ["notion.com"],
  "competitors": ["Confluence", "Coda", "ClickUp", "Obsidian"],
  "prompts": [
    "What are the best team wiki apps?",
    "Which note-taking app should a 20-person startup use?",
    "What is a good alternative to Confluence?"
  ],
  "models": ["~openai/gpt-luna-latest", "anthropic/claude-haiku-5.5", "perplexity/sonar"],
  "repeatsPerPrompt": 2,
  "monitorMode": true,
  "monitorName": "notion-weekly"
}
```

### Output (one row per answer)

```json
{
  "brand": "Notion",
  "prompt": "What are the best team wiki apps?",
  "model": "~openai/gpt-luna-latest",
  "repeat": 1,
  "status": "ok",
  "brand_mentioned": true,
  "brand_mention_count": 2,
  "brand_position": 1,
  "brand_list_rank": 1,
  "list_length": 5,
  "brand_first_mention_at": 0.12,
  "competitors_mentioned": ["Confluence", "Coda", "Obsidian"],
  "competitor_positions": {"Confluence": 2, "Coda": 3, "Obsidian": 4},
  "competitor_list_ranks": {"Confluence": 2, "Coda": 3, "Obsidian": 5},
  "share_of_voice": 0.333,
  "other_names_in_lists": ["Slab"],
  "cited_urls": [],
  "brand_domain_cited": false,
  "change": "moved up",
  "previous_mentioned": true,
  "previous_position": 2,
  "previous_list_rank": 2,
  "sentiment": "positive",
  "sentiment_reason": "Described as flexible and the easiest place to start.",
  "answer_text": "Here are some of the best note-taking and team wiki apps: 1. **Notion** ...",
  "answer_chars": 642,
  "input_tokens": 61,
  "output_tokens": 180,
  "model_cost_usd": 0.0004,
  "market": null,
  "checked_at": "2026-10-12T07:00:00Z"
}
```

| Field | Meaning |
|---|---|
| `brand_position` | The order in which the brand was first named, among your brand and the competitors you listed. 1 means named first. |
| `brand_list_rank` | The brand's place in the answer's ranked list (numbered, bulleted or headings). Empty when the answer has no list or the brand is not in it. |
| `brand_first_mention_at` | How far into the answer the brand first appears, from 0 (start) to 1 (end). |
| `share_of_voice` | Brand mentions divided by all mentions of the brand and the listed competitors. |
| `other_names_in_lists` | Product names in the answer's lists that are not your brand or a listed competitor. |
| `cited_urls`, `brand_domain_cited` | Links the model cited, and whether one is on your domains. |
| `change` | Monitor mode only: `first check`, `gained mention`, `lost mention`, `moved up`, `moved down`, `same` or `still not mentioned`. |
| `model_cost_usd` | The model cost OpenRouter reported for this answer, when it reports one. |

The run summary is saved as `SUMMARY` in the run's key-value store, with the visibility rate (share of answers that name the brand), average position, average list rank, share of voice, competitor mention rates and sentiment counts: overall, per model and per prompt. It also lists the other names seen most often and any model that failed.

### Pricing

$0.03 per answer, plus the model cost.

You pay for each answer row in the results. Failed calls are not output and not charged. The model cost is billed by Apify's OpenRouter proxy at OpenRouter's prices. Rough estimates from prompt sizes: under $0.001 per answer with Claude Haiku 5.5 or GPT Luna, and about $0.006 with Perplexity Sonar, which includes a web search. Example: 10 prompts x 3 models once a week is 30 answers, about $0.90 plus about $0.10 of model cost.

### How it works

1. Builds the list of prompts (yours, or written from the category, brand and first two competitors).
2. Sends each prompt to each model through the Apify OpenRouter proxy (`https://openrouter.apify.actor`), authorised with your own Apify token inside the run. The system prompt only asks for a direct, practical answer that names specific products. It never mentions your brand, so the answer is not steered.
3. Reads each answer with fixed rules (whole-word matching, list detection, link extraction). The numbers do not depend on another AI call. The optional sentiment rating is the only second call.
4. In monitor mode, each answer's result is saved under your monitor name in a named key-value store (`ai-visibility-tracker-state`) and compared on the next run.

A model id that OpenRouter rejects is skipped for the rest of the run and listed in the summary. Network errors and rate limits are retried with backoff.

### Limits

- Models answer differently each time and change over time. Use repeats and look at trends, not one answer.
- Models without web search (Claude Haiku, GPT Luna) answer from their training data, which is what many people see in chat apps. Perplexity Sonar searches the web live. Results in consumer apps can differ because those apps add their own search, memory and location.
- The proxy allows at most 2,048 output tokens per answer, which is enough for normal answers.
- Names are matched as written. Add other spellings and product names under "Other names for the brand".

### FAQ

**Do I need an OpenAI, Anthropic or Perplexity account?** No. The Apify OpenRouter proxy bills the model cost to your Apify account. You can use your own OpenRouter key instead.

**Which models can I use?** Any model id listed on openrouter.ai/models, for example `perplexity/sonar-pro` or `~deepseek/deepseek-flash-latest`.

**Why ask the same question more than once?** The same question can get a different list each time. Three asks per prompt give a more reliable visibility rate.

**Does the first monitor run show changes?** No. The first run sets the baseline (`first check`). Later runs show what changed.

**Can I track several brands?** Run once per brand with its own monitor name, or put one brand in Brand and the others in Competitors to compare them in the same answers.

# Actor input Schema

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

Your brand or product name, as people write it, e.g. Notion.

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

Other spellings or product names that count as a mention, e.g. Notion AI, notion.so.

## `brandDomains` (type: `array`):

Your domains, e.g. notion.com. Used to tell you when an answer links to your site.

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

Competitor names to track in the same answers. Each row shows which ones were named and in what order.

## `category` (type: `string`):

Used to write prompts for you when the Prompts field is empty, e.g. 'note-taking apps for teams' gives 'What are the best note-taking apps for teams?' and similar questions.

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

The questions your buyers ask an AI assistant, one per item. Leave empty to use 5 to 7 questions written from the category, brand and first two competitors. Each prompt is asked of every model.

## `models` (type: `array`):

OpenRouter model ids to ask. If empty: ~openai/gpt-luna-latest (OpenAI), anthropic/claude-haiku-5.5 (Anthropic) and perplexity/sonar (Perplexity, live web search with citations). Any id from openrouter.ai/models works.

## `repeatsPerPrompt` (type: `integer`):

AI answers change from one ask to the next. Ask 3 times for a steadier visibility rate. Each answer is billed.

## `market` (type: `string`):

Adds 'I am based in ...' to every question, e.g. United Arab Emirates or Germany, to see local answers.

## `judgeSentiment` (type: `boolean`):

When the brand is named, a second, small model call rates the description as positive, neutral or negative and gives a one-line reason.

## `judgeModel` (type: `string`):

OpenRouter model id used for the sentiment rating.

## `monitorMode` (type: `boolean`):

Remember each answer's result and, on later runs, add what changed: gained mention, lost mention, moved up, moved down, same. Schedule it weekly to track your visibility.

## `monitorName` (type: `string`):

Keeps a separate history for each name. Use one name per brand or client.

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

Add the model's full answer to each row. Turn off for smaller rows.

## `maxAnswers` (type: `integer`):

Stop after this many answers (prompts x models x repeats). Each answer is billed.

## `llmProvider` (type: `string`):

With the Apify proxy, model usage is added to your Apify bill. With your own key, OpenRouter bills you directly.

## `openrouterApiKey` (type: `string`):

Only needed when the provider is 'My own OpenRouter key'.

## `maxOutputTokens` (type: `integer`):

Upper limit for each answer. The Apify proxy allows at most 2,048.

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

How many model calls run at the same time.

## Actor input object example

```json
{
  "brand": "Notion",
  "brandAliases": [
    "Notion AI"
  ],
  "brandDomains": [
    "notion.com",
    "notion.so"
  ],
  "competitors": [
    "Confluence",
    "Coda",
    "ClickUp",
    "Obsidian"
  ],
  "category": "note-taking and team wiki apps",
  "prompts": [
    "What are the best team wiki apps?",
    "What is a good alternative to Confluence?"
  ],
  "models": [
    "~openai/gpt-luna-latest",
    "anthropic/claude-haiku-5.5",
    "perplexity/sonar"
  ],
  "repeatsPerPrompt": 1,
  "judgeSentiment": false,
  "judgeModel": "anthropic/claude-haiku-5.5",
  "monitorMode": true,
  "monitorName": "default",
  "includeAnswerText": true,
  "maxAnswers": 600,
  "llmProvider": "apify",
  "maxOutputTokens": 1200,
  "maxConcurrency": 4
}
```

# Actor output Schema

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

One row per answer: one prompt asked of one model once. Read the rows with GET on this URL.

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

Visibility rate per model and prompt, share of voice and any model errors.

# 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",
    "brandAliases": [
        "Notion AI"
    ],
    "brandDomains": [
        "notion.com",
        "notion.so"
    ],
    "competitors": [
        "Confluence",
        "Coda",
        "ClickUp",
        "Obsidian"
    ],
    "category": "note-taking and team wiki apps",
    "prompts": [
        "What are the best team wiki apps?",
        "What is a good alternative to Confluence?"
    ],
    "models": [
        "~openai/gpt-luna-latest",
        "anthropic/claude-haiku-5.5",
        "perplexity/sonar"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("brightknot/ai-search-visibility-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": "Notion",
    "brandAliases": ["Notion AI"],
    "brandDomains": [
        "notion.com",
        "notion.so",
    ],
    "competitors": [
        "Confluence",
        "Coda",
        "ClickUp",
        "Obsidian",
    ],
    "category": "note-taking and team wiki apps",
    "prompts": [
        "What are the best team wiki apps?",
        "What is a good alternative to Confluence?",
    ],
    "models": [
        "~openai/gpt-luna-latest",
        "anthropic/claude-haiku-5.5",
        "perplexity/sonar",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("brightknot/ai-search-visibility-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": "Notion",
  "brandAliases": [
    "Notion AI"
  ],
  "brandDomains": [
    "notion.com",
    "notion.so"
  ],
  "competitors": [
    "Confluence",
    "Coda",
    "ClickUp",
    "Obsidian"
  ],
  "category": "note-taking and team wiki apps",
  "prompts": [
    "What are the best team wiki apps?",
    "What is a good alternative to Confluence?"
  ],
  "models": [
    "~openai/gpt-luna-latest",
    "anthropic/claude-haiku-5.5",
    "perplexity/sonar"
  ]
}' |
apify call brightknot/ai-search-visibility-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,brightknot/ai-search-visibility-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/Hux1ZcuDq51fbLAFS/builds/Hy0siDYl7KAERRdMB/openapi.json
