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.
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.
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.
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
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
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
[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