# Apify Ai Search Visibility Tracker

> Track whether a brand and its competitors get cited or mentioned across Google AI Overviews, Google AI Mode, ChatGPT Search, Perplexity, Microsoft Copilot, and Google Gemini for a defined set of prompts, on a recurring schedule. Use when user asks to track AI visibility, monitor brand mentions in AI search, track ChatGPT citations, do AI search SEO tracking, GEO tracking (Generative Engine Optimi…

- **Type:** Skill
- **Install:** `agentstack add skill-apify-awesome-skills-apify-ai-search-visibility-tracker`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [apify](https://agentstack.voostack.com/s/apify)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [apify](https://github.com/apify)
- **Source:** https://github.com/apify/awesome-skills/tree/main/skills/apify-ai-search-visibility-tracker

## Install

```sh
agentstack add skill-apify-awesome-skills-apify-ai-search-visibility-tracker
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# AI Search Visibility Tracker

Four workflows covering the full AI visibility lifecycle: **discover** which prompts matter → **find** citation opportunities → **audit** your site → **track** over time.

All workflows use `apify/google-search-scraper` for AI search. Workflow C also uses `apify/website-content-crawler`.

**Recommended flow:** Run Workflow A to discover prompts → Workflow B to find citation opportunities → Workflow C to audit your site → Workflow D to track everything on a schedule.

---

## Workflow A — Competitor Prompt Discovery

**Goal:** Find which queries surface a competitor in AI search answers, so you know which prompts are worth monitoring.

### Inputs to collect

| # | Input | Notes |
|---|-------|-------|
| 1 | Competitor domain(s) | e.g. `brightdata.com`, `scraperapi.com` |
| 2 | Seed topic keywords | e.g. "web scraping", "data extraction API" |
| 3 | AI sources | Default: all six (AI Overviews, AI Mode, ChatGPT, Perplexity, Copilot, Gemini) |

### Workflow

1. Generate 15–30 candidate queries from seed keywords using these templates:
   - `best [topic]`, `[topic] tools`, `how to [topic]`, `[topic] for [use case]`
   - `[topic] vs [competitor brand]`, `[competitor brand] alternative`
   - `[topic] API`, `[topic] pricing`, `[topic] tutorial`

2. Run `apify/google-search-scraper` for each candidate query. For each result, extract:
   - `aiOverview.sources[]`, `aiMode.sources[]`, `chatGptAnswer.sources[]`, `perplexityAnswer.sources[]`, `copilotAnswer.sources[]`, `geminiAnswer.sources[]`
   - Also check `answer_text` / `aiOverview.text` for competitor brand name mentions (word-boundary match: `\bBrand\b`)

3. For each (query, source) pair where the competitor domain or brand appears: record a hit.

4. Output a prompt-major table sorted by total hit count descending:

```
| Query | ChatGPT | Perplexity | AI Overviews | AI Mode | Copilot | Gemini | Total |
|-------|---------|------------|--------------|---------|---------|--------|-------|
| "best web scraping API" | ✓ | ✓ | — | ✓ | — | ✓ | 4 |
| "how to scrape Google" | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 6 |
```

5. Deliver the top-N queries (default 10) as a ready-to-paste list for Workflow D's `config.json` prompts.

---

## Workflow B — Citation Opportunity Finder

**Goal:** For a target topic, identify which domains and content types AI engines most often cite — revealing where to publish or pitch content.

### Inputs to collect

| # | Input | Notes |
|---|-------|-------|
| 1 | Target topic / industry | e.g. "web scraping", "ecommerce automation" |
| 2 | Seed queries | 5–20 queries covering the topic space |
| 3 | AI sources | Default: all six |
| 4 | Deep-crawl top cited? | Optional: crawl top-3 cited pages with `website-content-crawler` for structure patterns |

### Workflow

1. Run `apify/google-search-scraper` for each seed query across selected AI sources.

2. Collect every URL from `sources[]` across all results. Normalise to registrable domain (`blog.example.com` → `example.com`).

3. Aggregate:
   - **By domain**: count citations, list which AI sources cite it, list which queries triggered it
   - **By content type**: infer from URL path patterns (docs → `/docs/`, `/reference/`; blog → `/blog/`; news → known news domains)

4. Rank by total citation count. Output:

```
Top-cited domains for "web scraping" (42 queries × 6 sources):
| Domain | Citations | AI Sources | Inferred type |
|--------|-----------|------------|--------------|
| docs.apify.com | 38 | ChatGPT, Perplexity, AI Mode | Documentation |
| scraperapi.com/blog | 21 | AI Overviews, Gemini | Long-form blog |
```

5. If deep-crawl enabled: run `apify/website-content-crawler` on the top-3 cited URLs per domain. From the markdown output, extract:
   - First heading that directly answers the query
   - Presence of code blocks in first 500 words
   - Word count
   - Whether an H2/H3 contains the exact query phrase

6. Summarise patterns: "AI engines in this topic prefer [long-form docs / short direct-answer posts]. Typical cited page: [word count range], [has/lacks direct-answer H2], [has/lacks code example above the fold]."

---

## Workflow C — GEO Website Audit

**Goal:** Check whether a specific website's content is structured for AI citation; compare it against what AI engines actually cite for your target prompts.

### Inputs to collect

| # | Input | Notes |
|---|-------|-------|
| 1 | Your website URL | e.g. `https://apify.com` |
| 2 | Target prompts | Use Workflow A output, or supply 5–10 directly |
| 3 | AI sources | Default: all six |

### Workflow

1. Run `apify/google-search-scraper` for each target prompt. For each (prompt × source) record whether your registrable domain appears in `sources[]`.

2. For prompts where your domain is **not** cited: identify the top-cited competitor URL for that prompt.

3. Run `apify/website-content-crawler` on:
   - Your most relevant page(s) for each un-cited prompt
   - The top-cited competitor page for each un-cited prompt

4. For each un-cited prompt, produce a gap card:

```
Prompt: "how to scrape Google search results"
Your page: apify.com/blog/scraping-google  →  NOT cited on ChatGPT, Perplexity, AI Mode
Top-cited: docs.brightdata.com/scraping/google (cited 5/6 sources)

Structural gaps:
  ✗ Your page: answer buried after 900 words, no direct-answer H2
  ✓ Competitor: H2 "How to scrape Google in 3 steps" at word 120 + code block at word 180

Recommended actions (priority order):
  1. Add H2 that mirrors the query phrase within first 300 words
  2. Move code example above the fold
  3. Add a "Quick answer" summary box at the top
```

5. Deliver: per-prompt gap cards + a consolidated action table ranked by expected impact.

---

## Workflow D — Recurring Visibility Tracker

**Goal:** Snapshot brand citations and mentions across all six AI surfaces on a recurring schedule and track changes over time.

### Prerequisites
(No need to check upfront)

- `APIFY_TOKEN` saved in a `.env` file next to `config.json` (the runner auto-loads it).
- Python 3.9+ on PATH. `pip3 install requests` (only third-party dependency); `pip3 install tldextract` recommended for accurate registrable-domain matching on multi-part TLDs.
- For automated daily runs: macOS / Linux with launchd or cron available (the installer handles both). Windows users get printed `schtasks` instructions.

### Steps

Copy this checklist and track progress:

```
Task Progress:
- [ ] Step 1: Load or collect the seven required inputs
- [ ] Step 2: Confirm AI sources and cadence
- [ ] Step 3: Write config.json + .env, then install the OS schedule
- [ ] Step 4: Run a snapshot now so the user sees the first report
- [ ] Step 5: Deliver the history report (diff vs. all prior runs)
```

### Step 1: Load or Collect the Seven Required Inputs

If `config.json` exists in the user's working directory, load it and skip to Step 4 unless the user asks to reconfigure. On first run, ask all seven anchors **before** any Actor call:

| # | Input | Why it matters |
|---|-------|----------------|
| 1 | **Brand URL** | Primary domain. Drives registrable-domain citation matching (`blog.apify.com` -> `apify.com`). |
| 2 | **Brand name(s)** | Surface forms for text-mention matching (e.g., `Apify`, `apify.com`, `@apify`). URL-only matching misses mentions without links. |
| 3 | **Competitor brands** | Ask explicitly: *"Which competitors do you want tracked alongside your brand?"* Accept `name + domain` pairs. Zero is allowed; the question must still be asked on first run. |
| 4 | **Prompts to monitor** | One or more search queries. Each runs through every enabled AI source. If you don't know which prompts to use yet, run Workflow A first — it discovers competitor-visible prompts you can paste here. |
| 5 | **Cadence** | `daily` / `weekly` / `monthly`. Drives the schedule entry that `install_cron.sh` writes. |
| 6 | **Which AI sources** | Present the six (AI Overviews, AI Mode, ChatGPT, Perplexity, Copilot, Gemini), all enabled by default. Each adds per-result cost -- current pricing on the Actor page (https://apify.com/apify/google-search-scraper). |
| 7 | **Apify Dataset name** | The named dataset to append to. If absent, created on first run; the name is recorded in `config.json`. |

After those seven, ask optional follow-ups: `countryCode`, `languageCode`, `location` (UULE), preferred run hour (default `09:00` local).

Then one verbosity question -- save as `config.json:include_full_answers`:

- **`on_demand`** (default): report shows short quoted snippets around each surface-form match. Full LLM answers live in the named KV store; user can ask later.
- **`always`**: report embeds the full LLM answer verbatim whenever any entity is mentioned. Useful for one prompt; gets unwieldy at 5+ prompts.

### Step 2: Confirm AI Sources and Cadence

Echo back the user's seven choices in a single paragraph for confirmation. If the user toggles sources, update the in-memory config before writing.

### Step 3: Write `config.json` + `.env`, Then Install the OS Schedule

Create the working directory layout next to where the user wants reports to land:

```
working-dir/
  config.json     # copied from the skill's config.example.json, edited with collected values
  .env            # APIFY_TOKEN=apify_api_xxx   (chmod 600)
```

```bash
cp ${CLAUDE_PLUGIN_ROOT}/reference/scripts/config.example.json ./config.json
# then edit with the collected values, save

echo 'APIFY_TOKEN=your_token_here' > ./.env
chmod 600 ./.env
```

Then install the OS schedule:

```bash
bash ${CLAUDE_PLUGIN_ROOT}/reference/scripts/install_cron.sh --cadence daily --hour 9
```

Cron expression mapping:

| Cadence | Cron expression | When |
|---------|-----------------|------|
| daily   | `0 H * * *`     | every day at H:00 local |
| weekly  | `0 H * * 1`     | every Monday at H:00 |
| monthly | `0 H 1 * *`     | the 1st of every month at H:00 |

### Step 4: Run a Snapshot Now

**macOS:**

```bash
launchctl kickstart "gui/$(id -u)/com.apify.ai-visibility-tracker"
tail -f ~/Library/Logs/ai-visibility-tracker.log
```

**Linux / generic:**

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/reference/scripts/run_snapshot.py --config ./config.json
```

Both paths:

1. Call `apify/google-search-scraper` with the configured prompts + AI-source toggles.
2. Parse each (prompt x source) cell for citations and brand/competitor mentions.
3. Append rows to the named Apify Dataset (schema in `reference/output-schema.md`).
4. Write the raw item to the named KV store (long-term archive).
5. Compute the history vs. all prior runs.
6. Write `reports/snapshot-.md` next to `config.json`.

### Step 5: Deliver the History Report

Open `reports/snapshot-.md` and surface the top findings in chat. Lead with:

- **First-ever citations / mentions today** -- entity x source combinations crossing the threshold for the first time.
- **Drops** -- entity was cited in the latest prior run but isn't today.
- For every cited entity, the **exact matched URL(s)**.

### Output format

Snapshot summaries are **entity-major**: one Markdown table per tracked entity (brand, then competitors), one row per AI source, columns `Source | Cited | Mentioned | SoV% | Matched URLs | History`.

---

## Actors

| Actor | Used in | Key input fields |
|-------|---------|-----------------|
| `apify/google-search-scraper` | A, B, C, D | `queries[]`, `aiOverview`, `chatGptSearch`, `perplexitySearch`, `googleAiMode`, `bingCopilotSearch`, `googleGeminiSearch` |
| `apify/website-content-crawler` | B (optional), C | `startUrls[]`, `maxCrawlPages`, `outputMarkdown: true` |

Pricing changes; check the [pricing tab](https://apify.com/apify/google-search-scraper) before quoting numbers. Disable unused AI sources to reduce cost.

**Telemetry.** Workflow D runs through `reference/scripts/run_snapshot.py`, which already sets `User-Agent: apify-awesome-skills/ai-search-visibility-tracker-...` on every Apify API call. When calling these Actors ad-hoc in Workflows A-C, pass the matching flags so usage is attributed: `--user-agent apify-awesome-skills/apify-ai-search-visibility-tracker` and `--json` (use `--format json` for `apify datasets get-items`), and append `2>/dev/null`. Example:

```bash
apify actors call apify/google-search-scraper \
  --user-agent apify-awesome-skills/apify-ai-search-visibility-tracker \
  --json 2>/dev/null
```

## Quality Rules

- **Non-interactive.** No stdin reads in `run_snapshot.py` -- launchd / cron has no stdin.
- **Word-boundary brand matching** (`\bbrand\b`, case-insensitive). See `reference/citation-matching.md`.
- **Registrable-domain citation matching** (`blog.apify.com` counts as `apify.com`). See `reference/citation-matching.md`.
- **Never skip a row.** If an AI source returns nothing, write a row with `cited: false, mentioned: false, answer_text: "[no answer returned]"`.
- **Every row carries the Apify run ID** so any finding can be reverified.

## Error Handling

`APIFY_TOKEN not found` -- Tell the user to put it in `.env` next to `config.json` (`echo 'APIFY_TOKEN=...' > .env && chmod 600 .env`). Token at https://console.apify.com/account/integrations.
`config.json not found` -- Run Step 3 first to create it from the template.
`Dataset name not set` -- Ask the user for a name; the runner will create the dataset on first append.
`Actor run FAILED` -- Print the console link from the runner output and ask the user to inspect it.
`AI source returned no answer` -- The row is still written with `[no answer returned]`. Not an error.
`website-content-crawler returns no markdown` -- Page may be JS-heavy; try with `useBrowserCrawler: true`.
`Schedule not firing` -- See `reference/scheduling.md` troubleshooting section.
`No previous run to diff against` -- First run only. The report renders the snapshot without a history section.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [apify](https://github.com/apify)
- **Source:** [apify/awesome-skills](https://github.com/apify/awesome-skills)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-apify-awesome-skills-apify-ai-search-visibility-tracker
- Seller: https://agentstack.voostack.com/s/apify
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
