# AI Visibility Tracker (`agarwoods/ai-visibility-tracker`) Actor

- **URL**: https://apify.com/agarwoods/ai-visibility-tracker.md
- **Developed by:** [Ishaan Pare](https://apify.com/agarwoods) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $17.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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Visibility Tracker

Find out whether AI models mention your brand when people ask about your market. Give it your brand, your competitors and your prompts. It asks each model through the vendor's official API, using your own API keys, and reports whether and how your brand appears, at $20 per 1,000 checks.

It calls the official APIs of **OpenAI, Anthropic, Google Gemini and Perplexity**. You choose which ones by adding a key for each.

### What you get

- One row for each prompt and model, with the full answer
- Whether your brand or an alias was mentioned, how often, and where it first appeared
- Your rank among the brand names you listed, by first appearance
- Which of your competitors were mentioned
- The source links the API returned, and whether any of them point to your website
- Input and output token counts, so you can see what the vendor is likely to bill you for
- One free summary row per run, with visibility share per model and per competitor

### Sample output

One check:

```json
{
  "recordType": "check",
  "prompt": "What are the best note-taking apps for small teams?",
  "provider": "openai",
  "model": "gpt-6-luna",
  "brandMentioned": true,
  "mentionCount": 2,
  "firstMentionPosition": { "character": 42, "rank": 2 },
  "competitorsMentioned": ["Evernote"],
  "brandCited": true,
  "citations": ["https://www.notion.so/product", "https://reviews.example.com/apps"],
  "answer": "For small teams, Evernote is popular, and Notion is a strong choice. Many teams pick Notion for docs.",
  "inputTokens": 75,
  "outputTokens": 186,
  "status": "ok",
  "reason": null,
  "checkedAt": "2026-11-02T10:00:00.000Z"
}
```

The summary row for a run:

```json
{
  "recordType": "summary",
  "overallVisibilityShare": 0.25,
  "checksDelivered": 4,
  "checksFailed": 0,
  "visibilityByModel": [
    { "provider": "openai", "model": "gpt-6-luna", "checks": 2, "failed": 0, "brandMentions": 1, "visibilityShare": 0.5, "brandCitations": 2, "citedShare": 1 },
    { "provider": "gemini", "model": "gemini-3.8-flash", "checks": 2, "failed": 0, "brandMentions": 0, "visibilityShare": 0, "brandCitations": 0, "citedShare": 0 }
  ],
  "visibilityByCompetitor": [
    { "name": "Evernote", "checks": 4, "mentions": 4, "visibilityShare": 1 }
  ],
  "checkedAt": "2026-11-02T10:00:00.000Z"
}
```

These examples show the format, with made-up answers. Every row also has the other fields set to null, so the columns stay the same. `character` counts from 0, and a visibility share runs from 0 to 1.

### How to use it

1. Enter your brand name, any aliases, your website domain and your competitors.
2. Add your prompts, one per line, such as "What are the best note-taking apps for small teams?"
3. Add an API key for each vendor you want to check, set Maximum checks, and press Start.

Each prompt sent to each model is one check, so 10 prompts and 4 models make 40 checks.

Model names change often. The defaults favour low cost, and you can type any current name from the vendor's documentation. For Perplexity, you can enter an effort level, such as fast, or a model ID with a slash, such as perplexity/sonar.

### Pricing

You pay Apify for this Actor, and **you pay each AI vendor separately**.

| Event | Price |
| --- | --- |
| Each check delivered | $0.02 ($20 per 1,000) |
| Each run started | $0.00005 |

Status rows, the summary row and failed checks are free. A check counts as delivered when a model returned an answer, including an answer the vendor cut short, which is marked truncated. A run with Maximum checks left at 50 costs at most about $1.00 here, plus what your vendors bill.

**AI vendor bills:** OpenAI, Anthropic, Google and Perplexity bill your own keys for the tokens and web searches you use. Web search has its own fee at some vendors. This Actor has no control over those prices, and a check that fails or gets retried may still appear on your vendor bill. Check your usage limits with each vendor before a big run.

On the free Apify plan, each run delivers up to 100 checks, and a status row tells you when you reach that limit. Any paid Apify plan removes it.

### Status rows

The Actor always tells you what happened. Check rows have a status of ok, truncated or failed. Other messages arrive as free rows with recordType set to status:

| Status | Meaning |
| --- | --- |
| no_keys | No API keys were given, so nothing was asked |
| no_prompts | The prompt list is empty, so nothing was asked |
| no_brand | No brand name was given, so nothing was asked |
| missing_key | One vendor was skipped, because it has no key |
| bad_key | The vendor refused the key, so no more requests went to it in this run |
| model_rejected | The vendor refused the model name or its web search tool, so no more requests went to it in this run. Other vendors carry on |
| quota_exceeded | The vendor says your account is out of credit or over a limit, so no more requests went to it in this run |
| provider_stopped | 3 checks in a row failed on this vendor, so no more requests went to it in this run |
| max_checks | The run reached your Maximum checks |
| free_limit | The run reached the free plan limit |
| spending_limit | The run reached your maximum cost |

If you only want answers, filter on recordType equal to check. Temporary errors from a vendor, such as rate limits and server errors, are retried with increasing waits before a check is marked failed.

### Your API keys

Keys use Apify's secret input, so they are stored encrypted. Each key is sent only to its own vendor's official API host, in a request header. Redirects are not followed. This Actor doesn't log, store or return your keys, and it removes them from vendor error messages before writing a row. The answers and prompts themselves are stored in your Apify dataset, and each vendor handles what you send it under its own terms. Requests ask OpenAI, Gemini and Perplexity not to store the response for later retrieval.

### Good to know

- **API answers can differ from what people see in the consumer apps.** ChatGPT, Claude, Gemini and Perplexity as apps can use different models, instructions, personalisation and search settings. This Actor shows what the API returns, not what any one person sees.
- **Answers vary between runs.** The same prompt can give a different answer minutes later, so one run is a sample, not a measurement. Run the same prompts repeatedly and compare the trend before you act on a small change.
- **Matching is literal.** It ignores case and only counts whole words, so Notion doesn't match notional, and a hyphen or punctuation next to a name still counts. It can't tell meanings apart, so a brand called Box also matches "in a box". It won't find translations or misspellings, so add those as aliases. Names in scripts written without spaces, such as Japanese, can be missed.
- **Rank only covers names you listed.** A rank of 1 means your brand came before your listed competitors, even if the answer named another brand first. Rank is empty if you list no competitors.
- **Overlapping names go to the longest match.** If your brand is Apple and a competitor is Apple Music, "Apple Music" counts only for the competitor.
- **Citations depend on the vendor.** Web search is turned on for every check, and it can bill your key for searches. Answers can still have no citations. For Perplexity, if an answer marks no sources, all of its search results are listed. Google can return its own redirect links, and those are matched to your domain by their title.
- **Sentiment isn't included**, because a simple rule would be too unreliable to be useful. The full answer is in each row, if you want to score it yourself.
- **One model per vendor per run.** To compare two models from one vendor, run the Actor twice.
- Your prompt is sent exactly as you wrote it, with no added instructions. Perplexity's effort levels add their own instructions on Perplexity's side.

### Using it with AI agents

Every field is optional, and the main inputs are lists. An agent can check many prompts in one call and read status rows to see what went wrong. Keep maxChecks low, because each check also costs money on your vendor keys.

### Data and use

This Actor calls only the official APIs you supply keys for. It uses no logins, cookies or scraping of consumer apps. You are responsible for following each vendor's terms and for how you use the results. This Actor is not affiliated with or endorsed by OpenAI, Anthropic, Google, Perplexity or any brand named in its examples.

### Support

Open an issue on the Issues tab with your input, leaving out your keys, and what you expected.

# Actor input Schema

## `brandName` (type: `string`):

The brand to look for in each answer. Matching ignores case and only counts whole words, so Art does not match smart.

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

Other names that count as your brand, one per line, such as a product name or a short form.

## `brandDomain` (type: `string`):

Your website, such as example.com. A citation counts as yours when its address is this domain or a subdomain of it. Leave empty and brandCited stays empty.

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

Competitor names to look for, one per line. They also set your rank: rank 1 means your brand was the first of these names to appear.

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

The questions to ask each model, one per line. Each prompt sent to each model is one check.

## `maxChecks` (type: `integer`):

Stop after this many checks are delivered. This caps what you pay for this Actor, and it also limits what the AI vendors bill to your keys. Failed checks do not count.

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

Your own OpenAI API key. It is stored encrypted, is only sent to api.openai.com, and is never logged or returned. Leave empty to skip OpenAI. OpenAI bills this key separately.

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

Your own Anthropic API key. It is stored encrypted, is only sent to api.anthropic.com, and is never logged or returned. Leave empty to skip Anthropic. Anthropic bills this key separately.

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

Your own Gemini API key. It is stored encrypted, is only sent to generativelanguage.googleapis.com, and is never logged or returned. Leave empty to skip Gemini. Google bills this key separately.

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

Your own Perplexity API key. It is stored encrypted, is only sent to api.perplexity.ai, and is never logged or returned. Leave empty to skip Perplexity. Perplexity bills this key separately.

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

The OpenAI model ID to ask. It must support the web search tool. If OpenAI rejects it, you get a status row and the other vendors carry on.

## `anthropicModel` (type: `string`):

The Claude model ID to ask. If Anthropic rejects it, you get a status row and the other vendors carry on.

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

The Gemini model ID to ask. It must support Google Search grounding. If Google rejects it, you get a status row and the other vendors carry on.

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

A Perplexity effort level (fast, low, medium, high or xhigh), or a model ID with a slash, such as perplexity/sonar. If Perplexity rejects it, you get a status row and the other vendors carry on.

## Actor input object example

```json
{
  "brandName": "Notion",
  "brandAliases": [
    "Notion AI"
  ],
  "brandDomain": "notion.so",
  "competitors": [
    "Evernote",
    "Obsidian"
  ],
  "prompts": [
    "What are the best note-taking apps for small teams?"
  ],
  "maxChecks": 50,
  "openaiModel": "gpt-6-luna",
  "anthropicModel": "claude-haiku-5-5",
  "geminiModel": "gemini-3.8-flash",
  "perplexityModel": "fast"
}
```

# Actor output Schema

## `checks` (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 = {
    "brandName": "Notion",
    "brandAliases": [
        "Notion AI"
    ],
    "brandDomain": "notion.so",
    "competitors": [
        "Evernote",
        "Obsidian"
    ],
    "prompts": [
        "What are the best note-taking apps for small teams?"
    ],
    "maxChecks": 50,
    "openaiModel": "gpt-6-luna",
    "anthropicModel": "claude-haiku-5-5",
    "geminiModel": "gemini-3.8-flash",
    "perplexityModel": "fast"
};

// Run the Actor and wait for it to finish
const run = await client.actor("agarwoods/ai-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 = {
    "brandName": "Notion",
    "brandAliases": ["Notion AI"],
    "brandDomain": "notion.so",
    "competitors": [
        "Evernote",
        "Obsidian",
    ],
    "prompts": ["What are the best note-taking apps for small teams?"],
    "maxChecks": 50,
    "openaiModel": "gpt-6-luna",
    "anthropicModel": "claude-haiku-5-5",
    "geminiModel": "gemini-3.8-flash",
    "perplexityModel": "fast",
}

# Run the Actor and wait for it to finish
run = client.actor("agarwoods/ai-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 '{
  "brandName": "Notion",
  "brandAliases": [
    "Notion AI"
  ],
  "brandDomain": "notion.so",
  "competitors": [
    "Evernote",
    "Obsidian"
  ],
  "prompts": [
    "What are the best note-taking apps for small teams?"
  ],
  "maxChecks": 50,
  "openaiModel": "gpt-6-luna",
  "anthropicModel": "claude-haiku-5-5",
  "geminiModel": "gemini-3.8-flash",
  "perplexityModel": "fast"
}' |
apify call agarwoods/ai-visibility-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,agarwoods/ai-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/CfZ7IV3pn8JZwTb8B/builds/1aMSp2iBkXXZNLmQf/openapi.json
