# AI Visibility & Share of Voice Tracker: ChatGPT, Perplexity (`steady-scrape/ai-brand-visibility-tracker`) Actor

Track how ChatGPT and Perplexity answer your buyer prompts: brand mentions, rank in the answer, cited URLs and domains, sentiment, share of voice vs competitors and week-over-week changes. Official APIs with web search, no browser. By SteadyScrape.

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

## Pricing

from $60.00 / 1,000 chatgpt 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 Visibility & Share of Voice Tracker: ChatGPT, Perplexity (by SteadyScrape)

Find out whether AI assistants recommend **your brand** when buyers ask questions like *"best CRM for a small business"*.
For every **prompt × engine**, this Actor asks the engine through its **official API with live web search** and returns:

- the answer (full text and a snippet) and the exact **model** that answered
- for your brand and each competitor: **mentioned?**, **mention count**, **rank** (order of first mention), **position in a list**, **sentiment** (positive, negative, neutral or mixed) and the surrounding context
- every **cited URL and domain**, and whether **your own domain was cited**
- **share of voice** across all checks and per engine, **visibility rate**, average rank and citation rate
- an optional **diff against your previous run** (visibility gained or lost, rank changes, citations gained or lost), so a weekly schedule becomes a tracker

Engines:

| Engine | How it is queried |
|---|---|
| `chatgpt` | OpenAI model (default `gpt-6-luna`) with OpenAI's native web search, via OpenRouter, or directly through the OpenAI Responses API + `web_search` (`chatgptProvider`) |
| `perplexity` | Perplexity Agent API with web search (`effort: low` by default) |

### Important: API answers are not the consumer apps

Answers come from the **official developer APIs**, not from chatgpt.com or perplexity.ai in a browser. Model choice, system
prompts, personalization, location, memory and search settings differ, so **the apps can give different answers** to the same
prompt. AI answers also change from run to run. Treat the results as a repeatable, comparable **sample** of AI visibility,
not as a screenshot of what any single user sees. Track trends over several prompts and runs instead of judging from one answer.

### Quick start

```json
{
  "prompts": ["best CRM for small business", "HubSpot alternatives for startups"],
  "brands": [{ "name": "HubSpot", "aliases": ["HubSpot CRM"], "domains": ["hubspot.com"] }],
  "competitors": ["Salesforce", { "name": "Zoho CRM", "aliases": ["Zoho"], "domains": ["zoho.com"] }, "Pipedrive"],
  "engines": ["chatgpt", "perplexity"],
  "trackingId": "crm-weekly"
}
```

Want to see the output format first? Set `"mockMode": true`. It returns synthetic answers, calls no AI provider and is never charged per check.

### Output

One dataset row per prompt × engine (`recordType: "check"`, `status: ok | error | skipped`), plus a final `recordType: "summary"`
row. The summary is also saved as `OUTPUT` in the run's key-value store. Key fields of a check row:

`prompt, engine, model, answerSnippet, answer, brandMentioned, bestBrandRank, brandDomainCited, trackedBrandsMentioned,
competitorsMentioned, brandsNamedInPrompt, brands[] {brand, isCompetitor, mentioned, mentionCount, rank, listPosition, sentiment, sentimentScore,
domainCited, citedBrandUrls, context}, citations[] {url, domain, title}, citedDomains, searchCalls, providerCostUsdEstimate, charged`

The summary contains `shareOfVoice`, `shareOfVoiceByEngine`, `topCitedDomains` and, when a previous run with the same
`trackingId` exists, `changes`.

**How metrics are computed.** Matching is case-insensitive and uses whole words on the brand name, its aliases and its domains,
after citation links and URLs are stripped from the answer. *Rank* is the order of first mention among all tracked names.
*Share of voice* is a brand's mentions divided by all tracked mentions. *Visibility rate* is the share of checks that mention
the brand. Brands named in the prompt itself are listed in `brandsNamedInPrompt`, because answers usually repeat them; use unbranded prompts to measure organic visibility. *Sentiment* is a simple lexicon score of the sentences that mention the brand, so it is a hint and not a judgment.

### Guardrails

- `maxChecks`: default 200, hard cap 2000 per run.
- `maxCostUsd`: estimated AI-provider cost cap per run, default $5. Checks that would go over it are skipped and not charged.
- Request timeouts and retries with backoff on 429 and 5xx errors. An invalid key or exhausted quota stops that engine for the rest of the run.
- Your *Max charge per run* setting is respected: only the checks it covers are run.
- **Failed and skipped checks are never charged.**

### Pricing

Pay per event. You only pay for checks that return an answer:

| Event | Price | What it is |
|---|---|---|
| `chatgpt-check` | **$0.06** | one prompt answered by ChatGPT (OpenAI model with live web search) and analyzed |
| `perplexity-check` | **$0.03** | one prompt answered by Perplexity (with live web search) and analyzed |
| Actor start | $0.00005 | Apify's standard start fee, once per run |

Example: 10 prompts × both engines = 10 × $0.06 + 10 × $0.03 = **$0.90** per run. A weekly tracker of 10 prompts costs about $3.90 a month.

- Failed, skipped and mock-mode checks are **never charged**. Every row has `charged` and `chargeEvent`. The run summary has
  `chargedEvents` and `chargedUsd`.
- Set *Max charge per run* in the run options to cap your spend. The Actor plans only the checks that fit under it, so it can
  still fit a $0.03 Perplexity check after a $0.06 ChatGPT check no longer fits.
- Try `mockMode: true` first. It shows the full output format for free.

### Scheduling and history

Give the run a `trackingId` and schedule it (for example weekly). Each run saves a compact snapshot in the named key-value
store `ai-brand-visibility-history` (configurable with `historyStoreName`) and compares itself with the previous snapshot.

### FAQ

**Is this scraping ChatGPT?** No. It uses official APIs: OpenAI models (through OpenRouter or OpenAI) and Perplexity.
**Can I pick the model?** Yes, within a cost-safe list: `openaiModel` and `perplexityModel` / `perplexityEffort`.
**Why did my brand show up in the app but not here (or the reverse)?** See *API answers are not the consumer apps* above.

### More tools from SteadyScrape

- [Keyword Search Volume](https://apify.com/steady-scrape/keyword-search-volume): Google search volume, CPC, competition and a 12-month trend for up to 10,000 keywords per run.
- [ATS Job Scraper](https://apify.com/steady-scrape/ats-job-feed): see which roles your competitors are hiring for, straight from their career sites.

***

Built and maintained by **SteadyScrape**. Not affiliated with OpenAI, OpenRouter or Perplexity.

# Actor input Schema

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

Questions your buyers ask AI assistants, one per line. Each prompt is sent to every selected engine (one check per prompt x engine).

## `brands` (type: `array`):

Brands to track. Plain names or objects with optional aliases and domains, e.g. {"name": "HubSpot", "aliases": \["HubSpot CRM"], "domains": \["hubspot.com"]}. Domains are used to detect when the engine cites your site.

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

Competitor brands, same format as your brands. Used for rank and share of voice.

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

Which AI engines to ask. Each engine adds one check per prompt.

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

Two-letter country code (US, GB, DE...) passed to web search as the approximate user location. Leave empty for no location. Applied on Perplexity and the direct OpenAI route; not sent on the OpenRouter route.

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

Save a snapshot in a named key-value store in your account and report changes since the last run with the same prompts, brands and engines (or the same tracking ID). Schedule the Actor weekly for week-over-week reports.

## `trackingId` (type: `string`):

Name for this tracking set, e.g. "acme-crm-weekly". Keeps history linked even when you edit prompts or brands. Default: derived from prompts, brands and engines.

## `historyStoreName` (type: `string`):

Named key-value store (in your Apify account) that keeps the previous-run snapshots.

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

OpenAI model for the ChatGPT engine (as openai/<model> with native web search on OpenRouter, or with the Responses API web_search tool on the OpenAI API). API answers can differ from the ChatGPT app.

## `chatgptProvider` (type: `string`):

How the ChatGPT engine is reached. auto = OpenRouter (OpenAI model with OpenAI's native web search) when the OPENROUTER_API_KEY secret is set, otherwise the OpenAI API directly.

## `openaiSearchContextSize` (type: `string`):

How much web search context the model gets (OpenAI search_context_size).

## `openaiMaxSearches` (type: `integer`):

Upper bound on web search calls per answer (OpenAI max_tool_calls). Applies to the direct OpenAI API route; on OpenRouter the native search plugin decides.

## `forceWebSearch` (type: `boolean`):

On: the model must search before answering (tool_choice required), so answers carry citations. Off: the model decides. Direct OpenAI API route only; the OpenRouter web plugin always searches.

## `perplexityEffort` (type: `string`):

Perplexity Agent API effort level (preset model, search and step settings).

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

Model override. auto uses the effort level's model.

## `includeFullAnswer` (type: `boolean`):

Off: only a 500-character snippet is stored per check.

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

Upper bound on prompt x engine checks in one run (hard cap 2,000).

## `maxCostUsd` (type: `number`):

Safety cap on estimated AI provider cost. New checks stop starting once the cap would be exceeded. Checks not run are not charged.

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

Upper bound on answer length (OpenAI max_output_tokens).

## `requestTimeoutSecs` (type: `integer`):

Per request timeout.

## `maxRetries` (type: `integer`):

Retries for timeouts, rate limits (429) and provider 5xx errors, with backoff.

## `concurrency` (type: `integer`):

How many checks run at the same time.

## `mockMode` (type: `boolean`):

Return synthetic answers without calling any AI provider. Useful to preview the output format. Mock checks are never charged.

## Actor input object example

```json
{
  "prompts": [
    "best CRM for small business",
    "HubSpot alternatives for startups"
  ],
  "brands": [
    {
      "name": "HubSpot",
      "aliases": [
        "HubSpot CRM"
      ],
      "domains": [
        "hubspot.com"
      ]
    }
  ],
  "competitors": [
    {
      "name": "Salesforce",
      "domains": [
        "salesforce.com"
      ]
    },
    {
      "name": "Zoho CRM",
      "aliases": [
        "Zoho"
      ],
      "domains": [
        "zoho.com"
      ]
    },
    {
      "name": "Pipedrive",
      "domains": [
        "pipedrive.com"
      ]
    }
  ],
  "engines": [
    "chatgpt",
    "perplexity"
  ],
  "compareWithPrevious": true,
  "historyStoreName": "ai-brand-visibility-history",
  "openaiModel": "gpt-6-luna",
  "chatgptProvider": "auto",
  "openaiSearchContextSize": "medium",
  "openaiMaxSearches": 2,
  "forceWebSearch": true,
  "perplexityEffort": "low",
  "perplexityModel": "auto",
  "includeFullAnswer": true,
  "maxChecks": 200,
  "maxCostUsd": 5,
  "maxOutputTokens": 2000,
  "requestTimeoutSecs": 120,
  "maxRetries": 2,
  "concurrency": 4,
  "mockMode": false
}
```

# Actor output Schema

## `checks` (type: `string`):

One row per prompt x engine with brand mentions, rank, citations and sentiment.

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

Share of voice per brand and engine, top cited domains and the diff against the previous run.

# 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 = {
    "prompts": [
        "best CRM for small business",
        "HubSpot alternatives for startups"
    ],
    "brands": [
        {
            "name": "HubSpot",
            "aliases": [
                "HubSpot CRM"
            ],
            "domains": [
                "hubspot.com"
            ]
        }
    ],
    "competitors": [
        {
            "name": "Salesforce",
            "domains": [
                "salesforce.com"
            ]
        },
        {
            "name": "Zoho CRM",
            "aliases": [
                "Zoho"
            ],
            "domains": [
                "zoho.com"
            ]
        },
        {
            "name": "Pipedrive",
            "domains": [
                "pipedrive.com"
            ]
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steady-scrape/ai-brand-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 = {
    "prompts": [
        "best CRM for small business",
        "HubSpot alternatives for startups",
    ],
    "brands": [{
            "name": "HubSpot",
            "aliases": ["HubSpot CRM"],
            "domains": ["hubspot.com"],
        }],
    "competitors": [
        {
            "name": "Salesforce",
            "domains": ["salesforce.com"],
        },
        {
            "name": "Zoho CRM",
            "aliases": ["Zoho"],
            "domains": ["zoho.com"],
        },
        {
            "name": "Pipedrive",
            "domains": ["pipedrive.com"],
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("steady-scrape/ai-brand-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 '{
  "prompts": [
    "best CRM for small business",
    "HubSpot alternatives for startups"
  ],
  "brands": [
    {
      "name": "HubSpot",
      "aliases": [
        "HubSpot CRM"
      ],
      "domains": [
        "hubspot.com"
      ]
    }
  ],
  "competitors": [
    {
      "name": "Salesforce",
      "domains": [
        "salesforce.com"
      ]
    },
    {
      "name": "Zoho CRM",
      "aliases": [
        "Zoho"
      ],
      "domains": [
        "zoho.com"
      ]
    },
    {
      "name": "Pipedrive",
      "domains": [
        "pipedrive.com"
      ]
    }
  ]
}' |
apify call steady-scrape/ai-brand-visibility-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steady-scrape/ai-brand-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/dohcVJkyrBBmrdXnr/builds/0hvGQctMh7LnmJ9No/openapi.json
