# Local AI Answer Visibility Checker: ChatGPT, Perplexity, Gemini (`shanmukhkiransagar/ai-answer-visibility-checker`) Actor

Does ChatGPT, Perplexity or Gemini recommend your local business, or your competitors? Bring your own API keys. Get share of voice, positions, cited sources, a citation-gap list and a client-ready report.

- **URL**: https://apify.com/shanmukhkiransagar/ai-answer-visibility-checker.md
- **Developed by:** [Shanmukh kiran Sagar](https://apify.com/shanmukhkiransagar) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 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?

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

## Local AI Answer Visibility Checker: ChatGPT, Perplexity & Gemini (Bring Your Own API Keys)

**Does AI recommend your local business, or your competitors?** Ask ChatGPT, Perplexity and Gemini the questions your
customers ask ("Who is the best emergency plumber in Leeds?") and see who gets named, in what position, which sources the
engines cite, and where you could get listed to show up next time. Uses **your own API keys** through the **official APIs**.
Nothing is scraped.

> **Bring your own API keys.** This actor calls OpenAI, Perplexity and Google with *your* keys. The providers bill you directly for that usage; the actor's own price covers the analysis and reports. **Pre-launch note:** the provider integrations have been tested end-to-end against mocked API responses that follow each provider's published documentation, not yet against the live APIs. Start with Demo mode or a small run (e.g. 2 questions × 1 engine) and tell us if anything looks off.

Built for **local service businesses** and the **agencies** that market them: plumbers, dentists, solicitors, salons,
roofers, estate agents, gyms and the like. Most AI visibility tools are built for SaaS brands. This one is built for "best
<trade> in <town>".

### What you get

For every question × engine:

| Field | Meaning |
|---|---|
| `brandMentioned` | Did the answer name your business? (name, aliases or domain, whole-word match) |
| `rank` / `rankMethod` | Your position: the list position if the answer is a list, otherwise the order in which tracked businesses were first named |
| `competitorsMentioned` | Competitors named, each with its position and whether its site was cited |
| `citedUrls` / `citedDomains` | Every source URL the engine cited |
| `brandDomainCited` | Was your own website cited as a source? |
| `brandSnippet` / `sentiment` | The sentence that names you, with a simple positive/neutral/negative/mixed keyword label |
| `answerText` | The full answer, for your records |
| `error` | Filled only when that check failed (bad key, rate limit…). Failed checks are not charged |

Plus these key-value store records for the whole run:

- **`REPORT_MD`**: a client-ready Markdown report with a headline, share-of-voice table, results by engine, "where competitors
  were recommended instead of you", the **citation-gap list**, quotes, and a plain-English method note. Add your agency
  name and it says "Prepared by <your agency>".
- **`CITATION_GAPS_CSV`**: the directories, review sites and articles the AI engines cited in answers that named competitors
  but not you, ranked by how often they appeared. This is your to-do list for getting listed. Competitors' own websites are
  flagged separately.
- **`SHARE_OF_VOICE_CSV`**: share of voice and mention rate for you and each competitor, overall and per engine.
- **`CHECKS_CSV`**: one row per check, ready for Sheets or Excel.
- **`SUMMARY`**: the same aggregates as JSON, for automations and AI agents.

### Use cases

- **Agency pitch or monthly report:** show a local client whether ChatGPT, Perplexity and Gemini name them for "best <trade> in <town>", and which competitors get named instead.
- **Citation-gap to-do list:** find the directories, review sites and articles the engines cite for competitors but not for you.
- **Before/after tracking:** re-run monthly with the same questions to see whether listings and content changes moved the needle.
- **Competitor benchmarking:** share of voice per engine for you and up to 25 competitors.

### How to use it

1. Enter your **business name**, website domain and up to 25 **competitors** (optionally `Name | domain.com`).
2. Enter your **trade** ("emergency plumber") and **town** ("Leeds"). The actor writes up to 10 buyer-style local questions
   for you, or you can paste your own.
3. Paste the API key for each engine you want: OpenAI, Perplexity and/or Gemini. Engines without a key are skipped.
   **Gemini note:** Google Search grounding is not available on the Gemini API free tier for Gemini 3.x models, so the
   default `geminiModel` (`gemini-3.8-flash`) needs a key from a **billing-enabled** Google AI Studio / Cloud project
   (Google currently includes a monthly allowance of grounded searches, then charges per search). A free-tier key returns
   HTTP 429 "exceeded your current quota". Older models such as `gemini-2.5-flash` are no longer offered to new Google
   users (HTTP 404), so for most people billing on the key's project is the only route.
4. Run. Open the **Storage → Key-value store → REPORT_MD** record and send it to your client, or download the CSVs.

Want to see the output first? Switch on **Demo mode**. It runs a sample plumber in Leeds against canned responses, needs no
keys, and is never charged.

#### Example input

```json
{
  "brandName": "Northgate Plumbing",
  "brandDomain": "northgateplumbing.co.uk",
  "brandAliases": ["Northgate"],
  "competitors": ["Aire Valley Plumbers | airevalleyplumbers.co.uk", "Leeds Heating Co", "QuickFix Drains"],
  "category": "emergency plumber",
  "location": "Leeds",
  "countryCode": "GB",
  "maxPrompts": 10,
  "providers": ["openai", "perplexity", "gemini"],
  "openaiApiKey": "sk-…",
  "perplexityApiKey": "pplx-…",
  "geminiApiKey": "AIza…",
  "agencyName": "Your Agency"
}
```

### How it works

| Engine | Official API used |
|---|---|
| ChatGPT | OpenAI **Responses API** with the hosted `web_search` tool. Your town and country are passed as the approximate `user_location`, and search is forced on by default so answers are grounded like a local search |
| Perplexity | Perplexity **Agent API** (`/v1/agent`, `fast` preset by default), which Perplexity documents as the replacement for Sonar |
| Gemini | Gemini **Interactions API** with **Grounding with Google Search** (default model needs a key from a billing-enabled Google project) |

Analysis runs locally: whole-word matching on your name, aliases and domain; list-position detection; citation domain
matching. There are no extra LLM calls, so the analysis costs you nothing and is repeatable.

### Pricing (pay per event)

| Event | Price | When it's charged |
|---|---|---|
| `visibility-check` | **$0.03 per check** ($30 per 1,000) | One question answered by one AI engine and analysed successfully |

- Example: 10 questions × 3 engines = 30 checks = **$0.90** per run (plus your own provider API usage, billed by the providers).
- Failed checks (bad key, timeouts, empty answers) and demo runs are **not charged**.
- The AI providers bill you directly for your own API usage, at their published rates, with no markup from us.
  Example: Google charges per grounded search query on Gemini 3+ models after a monthly free allowance. See each provider's
  pricing page.
- Set **Maximum cost per run** in Apify. The actor stops cleanly when it is reached and keeps everything collected so far.

### Your keys and data

- API keys are **secret inputs**: Apify encrypts them, and they are never written to the dataset, the report or the logs
  (error messages are redacted).
- The actor sends only your questions to the providers you select. It collects no personal data.

### Honest limitations

- **Live provider calls are not yet tested.** Request and response handling for OpenAI, Perplexity and Gemini follows each provider's documentation as of October 2026 and passes end-to-end tests with mocked responses, but has not been run against the live APIs yet. API shapes and model names change; if a provider rejects a request, the check fails (and isn't charged) and the error is shown in the dataset.

- **Default model names** (`openaiModel`, `perplexityPreset`, `geminiModel`) can be retired by the providers. You can change them in the input.

- AI answers **vary** between runs, accounts, locations and over time. API answers can differ from what someone sees in the
  ChatGPT, Perplexity or Gemini apps. Treat each run as a snapshot and re-run (e.g. monthly) to track change.
  This tool reports what the engines said. It does not guarantee or "rank" anything.

- Mention detection is whole-word name matching. A very generic business name (e.g. "Best Plumbing") can produce false
  matches, so add a domain and specific aliases.

- Sentiment is a simple keyword heuristic on one sentence, not a judgement of the business.

### FAQ

**Why not scrape ChatGPT directly?** Automated extraction from chat interfaces breaks their terms. The official APIs with
web search return answers with structured citations.

**Can I use it for non-local brands?** Yes. Leave **Town** empty and the generated questions become general ("What are the
best CRM software options right now?"), or paste your own questions.

**Does it work in other countries?** Yes. Set the town and the 2-letter country code (e.g. `US`, `IE`, `AU`). Questions are
generated in English; paste your own for other languages.

***

Not affiliated with OpenAI, Perplexity or Google. ChatGPT, Perplexity and Gemini are trademarks of their owners.

# Actor input Schema

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

The business you want to check, as people would write it (e.g. "Northgate Plumbing").

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

Used to detect when the AI engines cite your own site (e.g. northgateplumbing.co.uk).

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

Short names or old names the AI might use instead (e.g. "Northgate").

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

Up to 25 competitor names. Optionally add the domain after a pipe: "Aire Valley Plumbers | airevalleyplumbers.co.uk".

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

What customers search for, e.g. "emergency plumber", "dentist", "wedding photographer". Used to auto-generate buyer-style questions when you give no prompts.

## `location` (type: `string`):

Set this for local businesses. Questions become local ("Who is the best emergency plumber in Leeds?") and the city is passed to OpenAI's web search as the user location.

## `countryCode` (type: `string`):

Optional, e.g. GB, US, IE. Passed to OpenAI web search as the approximate user location.

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

Buyer-style questions to ask each engine. Leave empty to auto-generate from the category and town.

## `maxPrompts` (type: `integer`):

Cap on questions per run. Each question is asked once per selected engine, so checks = questions × engines.

## `providers` (type: `array`):

Which engines to ask. An engine is skipped if you don't supply its key.

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

Your own key from platform.openai.com. Stored encrypted; you pay OpenAI directly.

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

Your own key from the Perplexity API console. Stored encrypted; you pay Perplexity directly.

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

Your own key from Google AI Studio. Stored encrypted; you pay Google directly.

## `agencyName` (type: `string`):

Adds "Prepared by <name>" to REPORT_MD so you can send it to a client.

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

Any Responses API model that supports the web_search tool.

## `perplexityPreset` (type: `string`):

Agent API preset. "fast" is the documented replacement for Sonar.

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

Any Gemini model that supports Grounding with Google Search. The default (Gemini 3.x) needs a key from a billing-enabled Google project, because search grounding is not on the free tier for 3.x.

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

Sets tool_choice to required so every ChatGPT answer is grounded in a live search (closer to what local searchers see).

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

Lower this if your API keys hit rate limits.

## `timeoutSecs` (type: `integer`):

How long to wait for each AI engine to answer one question before marking the check as failed (failed checks are not charged).

## `demoMode` (type: `boolean`):

Ignores your inputs and runs a sample local business against canned API responses so you can see the output format. Never charged.

## Actor input object example

```json
{
  "brandName": "Northgate Plumbing",
  "competitors": [
    "Aire Valley Plumbers | airevalleyplumbers.co.uk",
    "Leeds Heating Co"
  ],
  "category": "emergency plumber",
  "location": "Leeds",
  "maxPrompts": 10,
  "providers": [
    "openai",
    "perplexity",
    "gemini"
  ],
  "openaiModel": "gpt-5.5",
  "perplexityPreset": "fast",
  "geminiModel": "gemini-3.8-flash",
  "forceWebSearch": true,
  "maxConcurrency": 3,
  "timeoutSecs": 90,
  "demoMode": false
}
```

# Actor output Schema

## `results` (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": "Northgate Plumbing",
    "competitors": [
        "Aire Valley Plumbers | airevalleyplumbers.co.uk",
        "Leeds Heating Co"
    ],
    "category": "emergency plumber",
    "location": "Leeds"
};

// Run the Actor and wait for it to finish
const run = await client.actor("shanmukhkiransagar/ai-answer-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 = {
    "brandName": "Northgate Plumbing",
    "competitors": [
        "Aire Valley Plumbers | airevalleyplumbers.co.uk",
        "Leeds Heating Co",
    ],
    "category": "emergency plumber",
    "location": "Leeds",
}

# Run the Actor and wait for it to finish
run = client.actor("shanmukhkiransagar/ai-answer-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 '{
  "brandName": "Northgate Plumbing",
  "competitors": [
    "Aire Valley Plumbers | airevalleyplumbers.co.uk",
    "Leeds Heating Co"
  ],
  "category": "emergency plumber",
  "location": "Leeds"
}' |
apify call shanmukhkiransagar/ai-answer-visibility-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,shanmukhkiransagar/ai-answer-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/PevHbhxQbFnFBrzWp/builds/2wQA7X4e4k8WwnRoh/openapi.json
