Tutorial

How to Track AI Overview Citations on a Budget

Monitor which sites get cited in Google AI Overviews without expensive rank tracking tools. Python script using search API at $0.005/query.

Google AI Overviews now appear for a large share of informational queries, and getting cited in them drives significant traffic. Traditional rank trackers charge hundreds of dollars to monitor AI Overview citations. You can build your own tracker with a search API that returns AI Overview data in its SERP response. This tutorial builds a citation monitoring pipeline that checks your target keywords, records which domains get cited, and tracks your citation share over time. Each check costs $0.005 via the Scavio API.

Prerequisites

  • Python 3.9+ installed
  • requests library installed
  • A Scavio API key from scavio.dev
  • A list of keywords where you expect AI Overviews

Walkthrough

Step 1: Check if a query triggers an AI Overview

Search for a keyword and check whether the SERP response includes an AI Overview section. Not all queries trigger one, so this step filters your keyword list.

Python
import requests, os

API_KEY = os.environ['SCAVIO_API_KEY']

def check_ai_overview(query: str) -> dict:
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us'})
    data = resp.json()
    ai_overview = data.get('ai_overview', {})
    has_overview = bool(ai_overview and ai_overview.get('text'))
    citations = ai_overview.get('citations', []) if has_overview else []
    return {
        'query': query,
        'has_ai_overview': has_overview,
        'citation_count': len(citations),
        'cited_domains': [c.get('domain', '') for c in citations]
    }

result = check_ai_overview('what is the best crm for startups')
print(f'AI Overview: {result["has_ai_overview"]}, Citations: {result["citation_count"]}')

Step 2: Batch check keywords and find citation patterns

Run through your keyword list and collect citation data. Identify which domains get cited most frequently across your target keywords.

Python
import time
from collections import Counter

def batch_citation_check(keywords: list) -> dict:
    results = []
    domain_counts = Counter()
    for kw in keywords:
        data = check_ai_overview(kw)
        results.append(data)
        for domain in data['cited_domains']:
            domain_counts[domain] += 1
        time.sleep(0.3)
    return {
        'total_keywords': len(keywords),
        'with_ai_overview': sum(1 for r in results if r['has_ai_overview']),
        'top_cited_domains': domain_counts.most_common(10),
        'details': results
    }

keywords = [
    'what is the best crm for startups',
    'how to choose a crm 2026',
    'crm software comparison',
    'best free crm tools'
]
report = batch_citation_check(keywords)
print(f'{report["with_ai_overview"]}/{report["total_keywords"]} keywords have AI Overviews')

Step 3: Track your domain citation share

Calculate how often your domain appears in AI Overview citations compared to competitors. This is your AI Overview share of voice.

Python
def citation_share(report: dict, your_domain: str) -> dict:
    total_citations = sum(count for _, count in report['top_cited_domains'])
    your_citations = sum(count for domain, count in report['top_cited_domains']
                         if your_domain in domain)
    share = (your_citations / max(total_citations, 1)) * 100
    # Find keywords where you are cited
    your_keywords = [r['query'] for r in report['details']
                     if any(your_domain in d for d in r['cited_domains'])]
    # Find keywords where you are NOT cited
    missing_keywords = [r['query'] for r in report['details']
                        if r['has_ai_overview'] and
                        not any(your_domain in d for d in r['cited_domains'])]
    return {
        'your_domain': your_domain,
        'citation_share': round(share, 1),
        'your_citations': your_citations,
        'total_citations': total_citations,
        'cited_keywords': your_keywords,
        'missing_keywords': missing_keywords
    }

share = citation_share(report, 'yoursite.com')
print(f'Citation share: {share["citation_share"]}% ({share["your_citations"]}/{share["total_citations"]})')
print(f'Missing from: {share["missing_keywords"]}')

Step 4: Store historical data for trend tracking

Save daily citation data to track whether your AEO efforts are working over time. A simple JSON file stores the time series.

Python
import json
from datetime import date

def save_citation_history(report: dict, your_domain: str) -> None:
    history_file = 'citation_history.json'
    try:
        with open(history_file) as f:
            history = json.load(f)
    except FileNotFoundError:
        history = []
    share = citation_share(report, your_domain)
    history.append({
        'date': date.today().isoformat(),
        'citation_share': share['citation_share'],
        'your_citations': share['your_citations'],
        'total_citations': share['total_citations'],
        'keywords_checked': report['total_keywords'],
        'keywords_with_overview': report['with_ai_overview']
    })
    with open(history_file, 'w') as f:
        json.dump(history, f, indent=2)
    print(f'Saved citation data for {date.today()}')
    if len(history) > 1:
        prev = history[-2]['citation_share']
        curr = share['citation_share']
        delta = curr - prev
        print(f'Trend: {prev}% -> {curr}% ({delta:+.1f}%)')

Step 5: Calculate monitoring costs

Budget your AI Overview monitoring based on keyword count and check frequency. Daily checks of 100 keywords cost $15/month.

Python
def monitoring_budget(num_keywords: int, checks_per_day: int = 1) -> dict:
    daily_credits = num_keywords * checks_per_day
    monthly_credits = daily_credits * 30
    monthly_cost = monthly_credits * 0.005
    return {
        'keywords': num_keywords,
        'daily_checks': checks_per_day,
        'monthly_credits': monthly_credits,
        'monthly_cost': f'${monthly_cost:.2f}',
        'plan': '$30/7K' if monthly_credits <= 7000
            else '$100/28K' if monthly_credits <= 28000
            else '$250/85K',
        'vs_semrush': f'Semrush Position Tracking: $139.95+/mo'
    }

for n in [50, 100, 500]:
    b = monitoring_budget(n)
    print(f'{b["keywords"]} keywords: {b["monthly_cost"]}/mo ({b["plan"]} plan)')
print(f'Comparison: Semrush starts at $139.95/mo for similar tracking')

Python Example

Python
import os, requests, time, json
from collections import Counter
from datetime import date

API_KEY = os.environ['SCAVIO_API_KEY']

def check_ai_overview(query):
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us'})
    data = resp.json()
    aio = data.get('ai_overview', {})
    citations = aio.get('citations', []) if aio.get('text') else []
    return {'query': query, 'has_aio': bool(aio.get('text')),
            'domains': [c.get('domain', '') for c in citations]}

def main():
    keywords = ['best crm startups', 'crm comparison 2026', 'free crm tools']
    domain_counts = Counter()
    for kw in keywords:
        result = check_ai_overview(kw)
        print(f'{"+AIO" if result["has_aio"] else "-AIO"} {kw}: {len(result["domains"])} citations')
        for d in result['domains']:
            domain_counts[d] += 1
        time.sleep(0.3)
    print(f'\nTop cited domains:')
    for domain, count in domain_counts.most_common(5):
        print(f'  {domain}: {count}')
    print(f'Cost: ${len(keywords) * 0.005:.3f}')

if __name__ == '__main__':
    main()

JavaScript Example

JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;

async function checkAiOverview(query) {
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, country_code: 'us' })
  });
  const data = await resp.json();
  const aio = data.ai_overview || {};
  return {
    query,
    hasAio: Boolean(aio.text),
    domains: (aio.citations || []).map(c => c.domain || '')
  };
}

async function main() {
  const keywords = ['best crm startups', 'crm comparison 2026'];
  const domainCounts = {};
  for (const kw of keywords) {
    const r = await checkAiOverview(kw);
    console.log(`${r.hasAio ? '+AIO' : '-AIO'} ${kw}: ${r.domains.length} citations`);
    r.domains.forEach(d => { domainCounts[d] = (domainCounts[d] || 0) + 1; });
  }
  console.log('Top domains:', Object.entries(domainCounts).sort((a,b) => b[1]-a[1]).slice(0,5));
}

main().catch(console.error);

Expected Output

JSON
+AIO what is the best crm for startups: 4 citations
+AIO how to choose a crm 2026: 3 citations
+AIO crm software comparison: 5 citations
-AIO best free crm tools: 0 citations

3/4 keywords have AI Overviews
Citation share: 8.3% (1/12)
Missing from: ['crm software comparison']

50 keywords: $7.50/mo ($30/7K plan)
100 keywords: $15.00/mo ($30/7K plan)
500 keywords: $75.00/mo ($100/28K plan)

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.9+ installed. requests library installed. A Scavio API key from scavio.dev. A list of keywords where you expect AI Overviews. 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

Monitor which sites get cited in Google AI Overviews without expensive rank tracking tools. Python script using search API at $0.005/query.