Appearing in a Google AI Overview drives clicks even from position 8 or lower in organic results. Disappearing from an AI Overview can cut traffic overnight with no warning in Search Console. This monitor checks your target keywords daily for AI Overview presence and brand citations, alerting on gains and losses so you can react before traffic drops compound.
Prerequisites
- Python 3.8+
- requests library
- A Scavio API key from scavio.dev
- Target keywords and brand terms to monitor
Walkthrough
Step 1: Configure the AI Overview monitor
Set up keywords and brand terms to track in AI Overviews.
import os, requests, json, sqlite3
from datetime import datetime
API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}
CONFIG = {
'brand_terms': ['scavio', 'scavio.dev'],
'keywords': ['best search api for developers', 'serp api comparison 2026',
'google search api python', 'web scraping api alternative',
'ai agent search tool', 'mcp search server',
'reddit api for agents', 'tiktok data api']
}
db = sqlite3.connect('ao_monitor.db')
db.execute('''CREATE TABLE IF NOT EXISTS ao_checks (
keyword TEXT, checked_at TEXT, has_ao INTEGER,
brand_cited INTEGER, ao_snippet TEXT
)''')
db.commit()
print(f'Monitoring {len(CONFIG["keywords"])} keywords for AI Overview brand citations')Step 2: Check each keyword for AI Overview and brand citations
Query with include_ai_overview and parse the response for brand mentions.
def check_ao(keyword, brand_terms):
data = requests.post('https://api.scavio.dev/api/v1/search',
headers=SH, json={'query': keyword, 'country_code': 'us',
'include_ai_overview': True}).json()
ao = data.get('ai_overview', {})
ao_text = json.dumps(ao).lower() if ao else ''
cited = any(b.lower() in ao_text for b in brand_terms)
snippet = ao_text[:300] if ao else ''
now = datetime.now().isoformat()
db.execute('INSERT INTO ao_checks VALUES (?,?,?,?,?)',
(keyword, now, 1 if ao else 0, 1 if cited else 0, snippet))
db.commit()
return {'keyword': keyword, 'has_ao': bool(ao), 'cited': cited}
results = []
for kw in CONFIG['keywords']:
r = check_ao(kw, CONFIG['brand_terms'])
results.append(r)
icon = 'CITED' if r['cited'] else 'AO' if r['has_ao'] else '---'
print(f' [{icon:6}] {kw}')
print(f'\nCost: ${len(CONFIG["keywords"]) * 0.005:.3f}')Step 3: Detect changes from previous check
Compare today versus yesterday to find new citations and drops.
def detect_ao_changes(keyword):
rows = db.execute(
'SELECT has_ao, brand_cited, checked_at FROM ao_checks WHERE keyword=? ORDER BY checked_at DESC LIMIT 2',
(keyword,)).fetchall()
if len(rows) < 2:
return None
curr_ao, curr_cited = rows[0][0], rows[0][1]
prev_ao, prev_cited = rows[1][0], rows[1][1]
changes = []
if curr_ao and not prev_ao: changes.append('AO_APPEARED')
if not curr_ao and prev_ao: changes.append('AO_DISAPPEARED')
if curr_cited and not prev_cited: changes.append('BRAND_GAINED')
if not curr_cited and prev_cited: changes.append('BRAND_LOST')
return changes if changes else None
print('\nChanges detected:')
alerts = []
for kw in CONFIG['keywords']:
changes = detect_ao_changes(kw)
if changes:
alerts.append({'keyword': kw, 'changes': changes})
for c in changes:
priority = 'HIGH' if 'LOST' in c or 'DISAPPEARED' in c else 'INFO'
print(f' [{priority}] {kw}: {c}')
if not alerts:
print(' No changes from previous check.')Step 4: Generate daily summary report
Aggregate results into a dashboard-ready daily summary.
def daily_summary():
today = datetime.now().strftime('%Y-%m-%d')
rows = db.execute(
'SELECT keyword, has_ao, brand_cited FROM ao_checks WHERE checked_at LIKE ?',
(f'{today}%',)).fetchall()
total = len(rows)
ao_count = sum(1 for r in rows if r[1])
cited_count = sum(1 for r in rows if r[2])
print(f'\n=== AI Overview Monitor - {today} ===')
print(f' Keywords checked: {total}')
print(f' AI Overview present: {ao_count}/{total} ({ao_count/total*100:.0f}%)')
print(f' Brand cited: {cited_count}/{total} ({cited_count/total*100:.0f}%)')
print(f' Citation rate: {cited_count/ao_count*100:.0f}% of AOs' if ao_count else ' No AOs found')
print(f' Daily cost: ${total * 0.005:.3f}')
print(f' Monthly estimate: ${total * 0.005 * 30:.2f}')
# Keywords where brand is cited
cited_kws = [r[0] for r in rows if r[2]]
if cited_kws:
print(f'\n Brand cited in AO for:')
for kw in cited_kws:
print(f' - {kw}')
daily_summary()Python Example
import os, requests, json
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
def check_ao(keyword, brand):
data = requests.post('https://api.scavio.dev/api/v1/search',
headers=SH, json={'query': keyword, 'country_code': 'us', 'include_ai_overview': True}).json()
ao = data.get('ai_overview', {})
cited = brand.lower() in json.dumps(ao).lower() if ao else False
print(f'{keyword}: AO={bool(ao)}, cited={cited}. Cost: $0.005')
check_ao('best serp api 2026', 'scavio')JavaScript Example
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
async function checkAO(keyword, brand) {
const data = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: SH,
body: JSON.stringify({ query: keyword, country_code: 'us', include_ai_overview: true })
}).then(r => r.json());
const ao = data.ai_overview || {};
const cited = JSON.stringify(ao).toLowerCase().includes(brand.toLowerCase());
console.log(`${keyword}: AO=${!!data.ai_overview}, cited=${cited}`);
}
checkAO('best serp api 2026', 'scavio').catch(console.error);Expected Output
Monitoring 8 keywords for AI Overview brand citations
[CITED ] best search api for developers
[AO ] serp api comparison 2026
[--- ] google search api python
[AO ] web scraping api alternative
[CITED ] ai agent search tool
[AO ] mcp search server
[--- ] reddit api for agents
[AO ] tiktok data api
Cost: $0.040
=== AI Overview Monitor - 2026-05-19 ===
Keywords checked: 8
AI Overview present: 6/8 (75%)
Brand cited: 2/8 (25%)
Citation rate: 33% of AOs
Daily cost: $0.040
Monthly estimate: $1.20