Tutorial

How to Build GEO Citation Scanner with MCP

Scan Google AI Overviews for your brand citations across queries. MCP-powered pipeline tracks GEO performance at $0.005/query.

Generative Engine Optimization (GEO) success depends on whether AI Overviews cite your brand. This scanner queries target keywords, checks if AI Overviews appear, and logs whether your domain is cited. Run it daily to track citation share over time. Each query costs $0.005.

Prerequisites

  • Python 3.8+
  • requests library
  • A Scavio API key from scavio.dev
  • Target domain and keyword list

Walkthrough

Step 1: Query target keywords for AI Overviews

Search each keyword and check if an AI Overview is present in the SERP.

Python
import os, requests, json
from datetime import datetime

API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}

DOMAIN = 'yourbrand.com'
KEYWORDS = [
    'best project management tool 2026',
    'project management software comparison',
    'how to choose project management app',
    'project management for small teams',
    'agile project management tools',
]

def scan_keyword(query):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': query, 'country_code': 'us'}).json()
    ai_overview = data.get('ai_overview', data.get('answer_box', {}))
    has_aio = bool(ai_overview)
    cited = False
    if has_aio:
        aio_text = json.dumps(ai_overview).lower()
        cited = DOMAIN.lower() in aio_text
    organic = data.get('organic_results', [])
    org_position = None
    for i, r in enumerate(organic):
        if DOMAIN in r.get('link', ''):
            org_position = i + 1
            break
    return {'query': query, 'has_aio': has_aio, 'cited': cited, 'organic_pos': org_position}

results = []
for kw in KEYWORDS:
    r = scan_keyword(kw)
    results.append(r)
    status = 'CITED' if r['cited'] else ('AIO present' if r['has_aio'] else 'No AIO')
    print(f'  [{status:12}] {kw[:50]:50} | org: #{r["organic_pos"] or "-"}')
print(f'\nCost: ${len(KEYWORDS) * 0.005:.3f}')

Step 2: Calculate citation share metrics

Compute GEO metrics: citation rate, AIO frequency, and organic correlation.

Python
def geo_metrics(results):
    total = len(results)
    aio_count = sum(1 for r in results if r['has_aio'])
    cited_count = sum(1 for r in results if r['cited'])
    aio_rate = aio_count / total * 100 if total else 0
    citation_rate = cited_count / aio_count * 100 if aio_count else 0
    overall_rate = cited_count / total * 100 if total else 0
    organic_when_cited = [r['organic_pos'] for r in results if r['cited'] and r['organic_pos']]
    avg_org = sum(organic_when_cited) / len(organic_when_cited) if organic_when_cited else 0
    print(f'\n=== GEO Citation Report - {DOMAIN} ===')
    print(f'  Keywords scanned: {total}')
    print(f'  AI Overviews present: {aio_count} ({aio_rate:.0f}%)')
    print(f'  Your brand cited: {cited_count} ({overall_rate:.0f}% of all, {citation_rate:.0f}% of AIO)')
    if avg_org:
        print(f'  Avg organic position when cited: #{avg_org:.1f}')
    print(f'\n  Not cited but AIO present:')
    for r in results:
        if r['has_aio'] and not r['cited']:
            print(f'    - {r["query"][:50]}')
    return {'aio_rate': aio_rate, 'citation_rate': citation_rate}

geo_metrics(results)

Step 3: Track citation changes over time

Store daily scan results and compare against previous scans.

Python
def track_citations(results, history_file='geo_citations.json'):
    try:
        with open(history_file) as f:
            history = json.load(f)
    except FileNotFoundError:
        history = []
    today = {
        'date': datetime.now().strftime('%Y-%m-%d'),
        'domain': DOMAIN,
        'total': len(results),
        'aio_present': sum(1 for r in results if r['has_aio']),
        'cited': sum(1 for r in results if r['cited']),
        'details': results
    }
    history.append(today)
    with open(history_file, 'w') as f:
        json.dump(history, f, indent=2)
    print(f'\nSaved scan to {history_file}')
    if len(history) >= 2:
        prev = history[-2]
        delta_aio = today['aio_present'] - prev['aio_present']
        delta_cited = today['cited'] - prev['cited']
        print(f'  vs {prev["date"]}: AIO {delta_aio:+d}, Citations {delta_cited:+d}')
    print(f'  Run daily to build citation trend data')

track_citations(results)

Python Example

Python
import os, requests, json
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}

def scan_geo(query, domain):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': query, 'country_code': 'us'}).json()
    aio = data.get('ai_overview', {})
    cited = domain.lower() in json.dumps(aio).lower() if aio else False
    print(f'{"CITED" if cited else "not cited":10} | {query[:50]}')

for q in ['best project management tool 2026', 'agile tools comparison']:
    scan_geo(q, 'yourbrand.com')
print('Cost: $0.010')

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
async function scanGeo(query, domain) {
  const data = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST', headers: SH,
    body: JSON.stringify({ query, country_code: 'us' })
  }).then(r => r.json());
  const aio = data.ai_overview || {};
  const cited = JSON.stringify(aio).toLowerCase().includes(domain.toLowerCase());
  console.log(`${cited ? 'CITED' : 'not cited'} | ${query}`);
}
await scanGeo('best project management tool 2026', 'yourbrand.com');

Expected Output

JSON
  [CITED       ] best project management tool 2026            | org: #3
  [AIO present ] project management software comparison        | org: #5
  [CITED       ] how to choose project management app          | org: #2
  [No AIO      ] project management for small teams            | org: #4
  [AIO present ] agile project management tools                | org: #7

Cost: $0.025

=== GEO Citation Report - yourbrand.com ===
  Keywords scanned: 5
  AI Overviews present: 4 (80%)
  Your brand cited: 2 (40% of all, 50% of AIO)
  Avg organic position when cited: #2.5

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.8+. requests library. A Scavio API key from scavio.dev. Target domain and keyword list. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Scan Google AI Overviews for your brand citations across queries. MCP-powered pipeline tracks GEO performance at $0.005/query.