Google AI Overviews now appear for over 40% of informational queries in 2026. Being cited in an AI Overview drives significant click-through even when your page is not in the top 3 organic results. Tracking which queries cite your domain and which do not is the new ranking report. This tutorial builds a citation tracker that checks a list of target queries, records whether your domain appears in the AI Overview sources, and logs results over time for trend analysis. Each check costs one Scavio API credit at $0.005.
Prerequisites
- Python 3.9+ installed
- requests library installed
- A Scavio API key from scavio.dev
- A list of target queries relevant to your domain
Walkthrough
Step 1: Define target queries and domain
Set up a list of queries you want to track for AI Overview citations. These should be informational queries where you expect your content to be relevant.
import os, requests, json, time
from datetime import datetime
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
URL = 'https://api.scavio.dev/api/v1/search'
TARGET_DOMAIN = 'scavio.dev'
QUERIES = [
'best search api for developers',
'how to get google search results programmatically',
'serp api comparison 2026',
'web search api for ai agents',
'mcp search server',
]Step 2: Check AI Overview citation for a single query
Search with include_ai_overview enabled and check if the target domain appears in the AI Overview sources list.
def check_citation(query: str, domain: str) -> dict:
resp = requests.post(URL, headers=H,
json={'query': query, 'country_code': 'us', 'include_ai_overview': True})
data = resp.json()
ai = data.get('ai_overview', {})
sources = ai.get('sources', [])
cited = False
citation_position = None
for i, src in enumerate(sources):
if domain in src.get('link', ''):
cited = True
citation_position = i + 1
break
organic = data.get('organic_results', [])
organic_pos = None
for r in organic:
if domain in r.get('link', ''):
organic_pos = r.get('position')
break
return {
'query': query,
'ai_overview_present': bool(ai.get('text')),
'cited': cited,
'citation_position': citation_position,
'organic_position': organic_pos,
'total_ai_sources': len(sources),
'timestamp': datetime.now().isoformat(),
}Step 3: Batch check all queries and save results
Iterate through all target queries, check citations, and save the results to a JSON file for historical tracking.
def track_citations(queries: list, domain: str) -> list:
results = []
for q in queries:
result = check_citation(q, domain)
results.append(result)
status = 'CITED' if result['cited'] else 'MISSING'
print(f'[{status}] "{q}" | AI src #{result["citation_position"]} | Organic #{result["organic_position"]}')
time.sleep(0.5)
# Save snapshot
snapshot = {'date': datetime.now().strftime('%Y-%m-%d'), 'domain': domain, 'results': results}
filename = f'ai_citations_{snapshot["date"]}.json'
with open(filename, 'w') as f:
json.dump(snapshot, f, indent=2)
cited_count = sum(1 for r in results if r['cited'])
print(f'\nCitation rate: {cited_count}/{len(results)} ({cited_count/len(results)*100:.0f}%)')
print(f'Saved to {filename}')
return results
results = track_citations(QUERIES, TARGET_DOMAIN)Step 4: Compare citations over time
Load previous snapshots and compare citation status to detect gains and losses in AI Overview visibility.
def compare_snapshots(current_file: str, previous_file: str):
with open(current_file) as f:
current = json.load(f)
with open(previous_file) as f:
previous = json.load(f)
prev_map = {r['query']: r['cited'] for r in previous['results']}
print(f'Citation changes: {previous["date"]} -> {current["date"]}')
for r in current['results']:
was_cited = prev_map.get(r['query'], False)
is_cited = r['cited']
if is_cited and not was_cited:
print(f' [GAINED] {r["query"]}')
elif not is_cited and was_cited:
print(f' [LOST] {r["query"]}')
elif is_cited:
print(f' [KEPT] {r["query"]}')
# compare_snapshots('ai_citations_2026-05-16.json', 'ai_citations_2026-05-09.json')Python Example
import os, requests, time, json
from datetime import datetime
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
def check_ai_citation(query, domain):
resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'query': query, 'country_code': 'us', 'include_ai_overview': True})
data = resp.json()
sources = data.get('ai_overview', {}).get('sources', [])
cited = any(domain in s.get('link', '') for s in sources)
pos = next((i+1 for i, s in enumerate(sources) if domain in s.get('link', '')), None)
return {'query': query, 'cited': cited, 'position': pos}
queries = ['search api for developers', 'serp api python', 'web search mcp server']
for q in queries:
r = check_ai_citation(q, 'scavio.dev')
print(f'[{"CITED" if r["cited"] else "MISS"}] {q} (pos: {r["position"]})')
time.sleep(0.5)JavaScript Example
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
async function checkAICitation(query, domain) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us', include_ai_overview: true })
});
const data = await resp.json();
const sources = data.ai_overview?.sources || [];
const cited = sources.some(s => (s.link || '').includes(domain));
const pos = sources.findIndex(s => (s.link || '').includes(domain)) + 1 || null;
console.log(`[${cited ? 'CITED' : 'MISS'}] ${query} (pos: ${pos})`);
}
const queries = ['search api for developers', 'serp api python'];
(async () => { for (const q of queries) await checkAICitation(q, 'scavio.dev'); })();Expected Output
[CITED] "best search api for developers" | AI src #2 | Organic #4
[MISSING] "how to get google search results programmatically" | AI src #None | Organic #7
[CITED] "serp api comparison 2026" | AI src #1 | Organic #3
[MISSING] "web search api for ai agents" | AI src #None | Organic #12
[CITED] "mcp search server" | AI src #3 | Organic #2
Citation rate: 3/5 (60%)
Saved to ai_citations_2026-05-16.json