# AI Search Citation Monitor — Google AI Overview & AI Mode (`fre.d/ai-search-citations`) Actor

Track which sources Google's AI Overview cites for your keywords. One record per query: AI answer text and Markdown, cited URLs with position and domain, whether your target domains were cited, and the organic top 10. Built for GEO / AI visibility monitoring. Failed queries are not charged.

- **URL**: https://apify.com/fre.d/ai-search-citations.md
- **Developed by:** [Yosuke Sakaguchi](https://apify.com/fre.d) (community)
- **Categories:** SEO tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 query processeds

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 Search Citation Monitor — Google AI Overview & AI Mode

Find out which websites Google's AI answers cite for the keywords you care about. Give the Actor a list of search queries and, optionally, the domains you want to track (your site, your clients, your competitors). For every query it fetches the Google results page, extracts the **AI Overview** block, and returns the answer text, every cited source with its real URL and domain, a yes/no hit for each target domain, and the organic top 10 for comparison.

Use it for GEO (generative engine optimization) reporting, AI-visibility tracking, content gap analysis, or simply to see whether Google's AI sends traffic to you or to someone else.

### What you get

One dataset record per query and engine:

| Field | Type | Description |
|---|---|---|
| `query` | string | The query you passed in |
| `engine` | `google_ai_overview` | `google_ai_mode` | Which AI surface was checked |
| `countryCode`, `languageCode` | string | Localization used for the search |
| `hasAiAnswer` | boolean | Whether an AI answer was present on the page |
| `answerText` | string | null | Plain text of the AI answer as served in the results page |
| `answerMarkdown` | string | null | The same answer as Markdown, with inline links resolved to real URLs |
| `citations` | `{ position, url, domain, title, resolved }[]` | Sources cited by the AI answer in order of appearance. `domain` is without `www.` |
| `citedDomains` | string\[] | Unique cited domains, first-seen order |
| `targetDomainHits` | `{ domain, cited, positions }[]` | One entry per target domain from the input. Subdomains count as hits |
| `organicTop10` | `{ position, url, domain, title, resolved }[]` | Up to 10 organic results from the same page |
| `extractionMethod` | string | null | Which extraction path found the answer (useful when Google changes its markup) |
| `serpUrl` | string | The Google URL that was requested |
| `fetchedAt` | string | ISO 8601 timestamp |
| `status` | `ok` | `failed` | `skipped` | `failed` = blocked, network error or AI Mode unavailable; `skipped` = spending cap reached |
| `error` | string | Present only when `status` is not `ok` |

The run always finishes as **SUCCEEDED**, even when some queries fail. Check `status` per record instead of the run status.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `queries` | string\[] | required | Search queries. One record per query and engine |
| `targetDomains` | string\[] | `[]` | Domains to track, e.g. `["example.com", "competitor.io"]`. Protocol, `www.` and paths are stripped |
| `engines` | string\[] | `["google_ai_overview"]` | `google_ai_overview` and/or `google_ai_mode` (experimental, see below) |
| `countryCode` | string | `us` | Selects the Google domain (`google.com`, `google.co.uk`, `google.de`, ...) and the `gl` parameter |
| `languageCode` | string | `en` | Interface language (`hl`). Extraction is most reliable with `en` |
| `maxQueries` | integer | `100` | Safety cap on the number of queries processed |
| `resolveUrls` | boolean | `true` | Google serves result links as opaque redirect tokens. When on, the Actor follows each redirect (headers only) to get the real URL and domain |
| `proxyGroup` | `auto` | `GOOGLE_SERP` | `RESIDENTIAL` | `auto` | Which Apify Proxy group fetches the results page. `auto` prefers the Google SERP proxy |
| `maxConcurrency` | integer | `3` | Parallel result-page fetches (1–10) |
| `debugSaveHtml` | boolean | `false` | Store the raw HTML of every fetched page in the run's key-value store (`HTML-0001.html`, ...) |

Example input:

```json
{
  "queries": ["best project management software", "how to make sourdough bread"],
  "targetDomains": ["forbes.com", "kingarthurbaking.com"],
  "engines": ["google_ai_overview"],
  "countryCode": "us",
  "languageCode": "en"
}
```

### Output example

```json
{
  "query": "best project management software",
  "engine": "google_ai_overview",
  "countryCode": "us",
  "languageCode": "en",
  "hasAiAnswer": true,
  "answerText": "The top project management software platforms for 2026 depend on your team's size, workflow style, and budget:\nTop Software Picks\nClickUp\nBest for: All-in-one flexibility and custom workflows.\nKey features: 13+ task views (list, board, Gantt, timeline), built-in AI, and a generous free plan ...",
  "answerMarkdown": "The top project management software platforms for 2026 depend on your team's size, workflow style, and budget:\n\nTop Software Picks\n\n-   **ClickUp** [ClickUp](https://www.clickup.com/)\n    -   **Best for:** All-in-one flexibility and custom workflows.\n ...",
  "citations": [
    { "position": 1, "url": "https://www.clickup.com/", "domain": "clickup.com", "title": "ClickUp", "resolved": true },
    { "position": 2, "url": "https://www.forbes.com/advisor/business/software/best-project-management-software/", "domain": "forbes.com", "title": "10 Best Project Management Software of 2026 – Forbes Advisor", "resolved": true },
    { "position": 3, "url": "https://www.monday.com/", "domain": "monday.com", "title": "monday.com", "resolved": true },
    { "position": 4, "url": "https://www.reddit.com/r/projectmanagement/comments/1jwjw9s/what_project_management_tools_do_you_guys/", "domain": "reddit.com", "title": "What project management tools do you guys recommend? - Reddit", "resolved": true },
    { "position": 5, "url": "https://www.youtube.com/watch?v=v33m1aDCs1w&t=270", "domain": "youtube.com", "title": "Ranking the Best Project Management Tools of 2026", "resolved": true }
  ],
  "citedDomains": ["clickup.com", "forbes.com", "monday.com", "reddit.com", "youtube.com"],
  "targetDomainHits": [
    { "domain": "forbes.com", "cited": true, "positions": [2] },
    { "domain": "kingarthurbaking.com", "cited": false, "positions": [] }
  ],
  "organicTop10": [
    { "position": 1, "url": "https://www.icagile.com/resources/honest-review-of-6-personal-project-management-tools-with-kanban-view", "domain": "icagile.com", "title": "Honest Review of 6 Personal Project Management Tools ...", "resolved": true },
    { "position": 2, "url": "https://www.paymoapp.com/blog/project-management-software/", "domain": "paymoapp.com", "title": "20+ Best Project Management Software for 2026", "resolved": true }
  ],
  "extractionMethod": "heading:ai-overview",
  "serpUrl": "http://www.google.com/search?q=best+project+management+software&hl=en&gl=us&pws=0",
  "fetchedAt": "2026-10-15T04:40:12.345Z",
  "status": "ok"
}
```

A query where Google shows no AI Overview still returns `status: "ok"` with `hasAiAnswer: false` and the organic top 10, because "no AI answer for this keyword" is itself a monitoring result. A failed query looks like this and is **not charged**:

```json
{
  "query": "best project management software",
  "engine": "google_ai_mode",
  "status": "failed",
  "error": "ai_mode_not_available",
  "hasAiAnswer": false,
  "citations": [],
  "organicTop10": [],
  "fetchedAt": "2026-10-15T04:40:12.345Z"
}
```

### Pricing

Pay per event. You are charged only for records with `status: "ok"`.

| Event | Price | When |
|---|---|---|
| `query-processed` | $0.01 | One query checked on one engine and the results page parsed (with or without an AI answer) |

Monitoring 200 keywords once a day costs $1.00 per day. Failed queries (blocked page, network error, AI Mode unavailable) are free. You can cap spending per run with the *Maximum total charge* setting in Apify Console or the `maxTotalChargeUsd` API parameter; once the cap is reached, remaining queries are returned with `status: "skipped"`.

### Usage

#### Apify Console

Open the Actor, paste your keywords into *Search queries*, add your domains to *Target domains*, click *Start*, and download the dataset as JSON, CSV or Excel. Schedule the task daily or weekly to build a time series of AI citations.

#### JavaScript (apify-client)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('fre.d/ai-search-citations').call({
  queries: ['best project management software', 'how to make sourdough bread'],
  targetDomains: ['forbes.com', 'kingarthurbaking.com'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const r of items) {
  if (r.status !== 'ok') { console.warn(r.query, r.error); continue; }
  console.log(r.query, r.hasAiAnswer ? r.citedDomains.join(', ') : '(no AI Overview)');
  for (const hit of r.targetDomainHits) if (hit.cited) console.log('  cited:', hit.domain, 'at', hit.positions);
}
```

#### Python (apify-client)

```python
from apify_client import ApifyClient

client = ApifyClient(token=os.environ["APIFY_TOKEN"])

run = client.actor("fre.d/ai-search-citations").call(run_input={
    "queries": ["best project management software", "how to make sourdough bread"],
    "targetDomains": ["forbes.com", "kingarthurbaking.com"],
})

for r in client.dataset(run["defaultDatasetId"]).iterate_items():
    if r["status"] != "ok":
        print("failed:", r["query"], r["error"])
        continue
    print(r["query"], "->", r["citedDomains"] if r["hasAiAnswer"] else "(no AI Overview)")
```

#### AI agents via MCP

Add the Apify MCP server to your agent (Claude Desktop, Cursor, LangGraph, etc.) and allow this Actor:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=fre.d/ai-search-citations",
      "headers": { "Authorization": "Bearer <APIFY_TOKEN>" }
    }
  }
}
```

The agent can then call the tool with `{"queries": ["..."], "targetDomains": ["..."]}` and read `citedDomains`, `targetDomainHits` and `answerText` directly from the result.

#### HTTP API

```bash
curl -X POST "https://api.apify.com/v2/acts/fre.d~ai-search-citations/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["best project management software"], "targetDomains": ["forbes.com"]}'
```

### How it works

1. Queries are de-duplicated and combined with the selected engines.
2. Each results page is fetched through Apify Proxy (Google SERP proxy by default, residential as fallback) with your country and language settings.
3. The AI Overview block is located by a chain of strategies: known container attributes, then the "AI Overview" heading and its enclosing block, then a text-structure fallback. The strategy that succeeded is reported in `extractionMethod`.
4. Google serves result links as opaque redirect tokens. With `resolveUrls` on, the Actor follows each redirect (reading only the `Location` header, never downloading the target page) to obtain the real URL and domain.
5. The answer is converted to plain text and Markdown, citations are de-duplicated and numbered, and target domains are matched against the cited domains (subdomains included).

### Limitations and notes

- **Not an official Google API.** Results are scraped from the public results page and are a snapshot: AI Overviews vary by country, language, time of day and even between two requests. Treat single runs as samples and schedule repeated runs for trends.
- **Partial answer text.** Google serves only the collapsed part of the AI Overview in the HTML; text behind "Show more" is loaded later by the browser and is not included. Citations visible in the collapsed block are captured; sources that only appear after expanding may be missing.
- **AI Mode is experimental.** Google renders AI Mode (`udm=50`) in the browser. When the HTML does not contain an answer, the record is returned with `status: "failed"` and `error: "ai_mode_not_available"` and is not charged.
- **Markup changes.** Google changes its HTML frequently. If `hasAiAnswer` drops to `false` across all queries while Google clearly shows AI Overviews, open an issue; `extractionMethod` and `debugSaveHtml` exist to make the fix fast.
- **No personal data.** The Actor only processes public search result pages for the keywords you provide. It does not use cookies, Google accounts or logins, and it does not collect data about individuals. Keep your `queries` free of personal information.
- `languageCode` other than `en` works for fetching, but the AI Overview heading is recognised in a limited set of languages (en, ja, de, fr, es, it, pt, nl, zh, ko).

### Support

Open an issue on the Actor's *Issues* tab in Apify Store. Feature requests (Bing Copilot, Perplexity, ChatGPT search, historical comparison between runs) are welcome.

# Changelog

This Actor's version history is a separate document: https://apify.com/fre.d/ai-search-citations/changelog.md

# Actor input Schema

## `queries` (type: `array`):

Keywords to check. One output record is produced per query and engine. Each query/engine pair is a separate billable event.

## `targetDomains` (type: `array`):

Domains you want to monitor, e.g. your own site and competitors (apify.com, docs.example.org). For every query the Actor reports whether each domain was cited by the AI answer and at which positions. Subdomains count as a hit.

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

Which Google AI surfaces to check. 'google_ai_overview' is the AI Overview block on a normal results page. 'google_ai_mode' tries the dedicated AI Mode page (udm=50); it is experimental and returns status 'failed' with error 'ai_mode_not_available' when Google does not serve it without a browser.

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

Two-letter country code that selects the Google domain and the 'gl' parameter (us, gb, de, fr, jp, ...). AI Overview availability depends on the country.

## `languageCode` (type: `string`):

Interface language passed as the 'hl' parameter (en, de, ja, ...). The AI Overview heading is detected in the most common languages; use 'en' for the most reliable extraction.

## `maxQueries` (type: `integer`):

Safety cap on how many queries from the list are processed in this run.

## `resolveUrls` (type: `boolean`):

Google serves result links as opaque redirect tokens. When enabled, the Actor follows each redirect (headers only, no page download) to get the real URL and domain of every citation and organic result. Disable to save a few seconds per query; citations will then carry the Google redirect URL and 'resolved: false'.

## `proxyGroup` (type: `string`):

'auto' uses Apify's Google SERP proxy and falls back to residential proxies if the SERP group is not available on your plan. Pick one explicitly to force it.

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

How many result pages are fetched in parallel.

## `debugSaveHtml` (type: `boolean`):

Store the raw HTML of every fetched result page in the run's key-value store (keys HTML-0001.html, ...). Useful when reporting extraction problems; off by default.

## Actor input object example

```json
{
  "queries": [
    "best project management software",
    "how to make sourdough bread",
    "what is retrieval augmented generation"
  ],
  "engines": [
    "google_ai_overview"
  ],
  "countryCode": "us",
  "languageCode": "en",
  "maxQueries": 100,
  "resolveUrls": true,
  "proxyGroup": "auto",
  "maxConcurrency": 3,
  "debugSaveHtml": false
}
```

# Actor output Schema

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

All records as JSON. Each record has query, engine, hasAiAnswer, answerText, answerMarkdown, citations, citedDomains, targetDomainHits, organicTop10, fetchedAt and status.

## `citationsOnly` (type: `string`):

Same records reduced to query, engine, hasAiAnswer, citedDomains, citations and targetDomainHits.

## `resultsCsv` (type: `string`):

Flat CSV with query, engine, hasAiAnswer, citedDomains, status and error.

# 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 = {
    "queries": [
        "best project management software",
        "how to make sourdough bread",
        "what is retrieval augmented generation"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fre.d/ai-search-citations").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 = { "queries": [
        "best project management software",
        "how to make sourdough bread",
        "what is retrieval augmented generation",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("fre.d/ai-search-citations").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 '{
  "queries": [
    "best project management software",
    "how to make sourdough bread",
    "what is retrieval augmented generation"
  ]
}' |
apify call fre.d/ai-search-citations --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fre.d/ai-search-citations"
        }
    }
}
```

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/9OsTsmgvNBvfLR2CO/builds/Ba9LvxcoiEcrBqK3K/openapi.json
