Les Google AI Overviews apparaissent désormais dans plus de 30 % des requêtes commerciales, et les marques citées dans ces aperçus captent des clics sans figurer dans le top 10 traditionnel. Pour savoir si votre marque apparaît dans les AI Overviews, il faut vérifier chaque mot-clé cible avec le paramètre include_ai_overview et analyser la réponse pour détecter les mentions de marque. Ce tutoriel construit un tracker quotidien de visibilité GEO à 0,005 $ par vérification de mot-clé.
Prérequis
- Python 3.8+
- bibliothèque requests
- Une clé API Scavio depuis scavio.dev
- Une liste de mots-clés cibles à surveiller
Parcours
Étape 1: Paramétrer la configuration de suivi GEO
Définissez vos termes de marque et mots-clés cibles à surveiller pour les citations dans les 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'}
BRAND_TERMS = ['scavio', 'scavio.dev', 'scavio api']
KEYWORDS = ['best serp api 2026', 'search api for agents', 'google search api python',
'web scraping alternative', 'ai agent search tool']
db = sqlite3.connect('geo_visibility.db')
db.execute('''CREATE TABLE IF NOT EXISTS checks (
keyword TEXT, checked_at TEXT, has_ai_overview INTEGER,
brand_cited INTEGER, citation_text TEXT, position INTEGER
)''')
db.commit()
print(f'Tracking {len(KEYWORDS)} keywords for {len(BRAND_TERMS)} brand terms')Étape 2: Vérifier la présence dans les AI Overviews et les citations de marque
Interroger chaque mot-clé avec include_ai_overview et analyser les mentions de marque.
def check_keyword(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 in ao_text for b in brand_terms)
org = data.get('organic_results', [])
pos = next((r['position'] for r in org
if any(b in r.get('link', '').lower() for b in brand_terms)), None)
now = datetime.now().isoformat()
db.execute('INSERT INTO checks VALUES (?,?,?,?,?,?)',
(keyword, now, 1 if ao else 0, 1 if cited else 0,
ao_text[:500] if cited else '', pos))
db.commit()
return {'keyword': keyword, 'has_ao': bool(ao), 'cited': cited, 'position': pos}
for kw in KEYWORDS:
r = check_keyword(kw, BRAND_TERMS)
status = 'CITED' if r['cited'] else ('AO present' if r['has_ao'] else 'No AO')
print(f' {kw:35} | {status:12} | Organic: #{r["position"] or "-"}')Étape 3: Générer un rapport de visibilité quotidien
Agréger les résultats des vérifications en un score de visibilité GEO quotidien.
def daily_report():
today = datetime.now().strftime('%Y-%m-%d')
rows = db.execute(
"SELECT keyword, has_ai_overview, brand_cited, position FROM 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])
avg_pos = [r[3] for r in rows if r[3]]
print(f'\nGEO Visibility Report - {today}')
print(f' Keywords checked: {total}')
print(f' AI Overviews present: {ao_count}/{total} ({ao_count/total*100:.0f}%)')
print(f' Brand cited in AO: {cited_count}/{total} ({cited_count/total*100:.0f}%)')
if avg_pos:
print(f' Avg organic position: {sum(avg_pos)/len(avg_pos):.1f}')
print(f' Cost: ${total * 0.005:.3f}')
return {'date': today, 'total': total, 'ao_rate': ao_count/total, 'citation_rate': cited_count/total}
daily_report()Étape 4: Suivre les tendances des citations dans le temps
Comparer les scores quotidiens pour détecter les gains ou pertes de visibilité GEO.
def trend_report(days=7):
rows = db.execute(
'SELECT DATE(checked_at) as d, AVG(has_ai_overview), AVG(brand_cited) FROM checks GROUP BY d ORDER BY d DESC LIMIT ?',
(days,)).fetchall()
print(f'\nGEO Visibility Trend ({len(rows)} days):')
for date, ao_rate, cite_rate in rows:
bar_ao = '#' * int(ao_rate * 20)
bar_cite = '#' * int(cite_rate * 20)
print(f' {date} | AO: {ao_rate*100:5.1f}% {bar_ao:20} | Cited: {cite_rate*100:5.1f}% {bar_cite:20}')
if len(rows) >= 2:
change = (rows[0][2] - rows[-1][2]) * 100
direction = 'UP' if change > 0 else 'DOWN' if change < 0 else 'FLAT'
print(f' Citation rate {direction} {abs(change):.1f}pp over {len(rows)} days')
trend_report()Exemple Python
import os, requests, json
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
def check_geo(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)}, Brand cited={cited}. Cost: $0.005')
check_geo('best serp api 2026', 'scavio')Exemple JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
async function checkGeo(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}`);
}
checkGeo('best serp api 2026', 'scavio').catch(console.error);Sortie attendue
Tracking 5 keywords for 3 brand terms
best serp api 2026 | CITED | Organic: #4
search api for agents | AO present | Organic: #6
google search api python | No AO | Organic: #8
web scraping alternative | AO present | Organic: #12
ai agent search tool | CITED | Organic: #5
GEO Visibility Report - 2026-05-19
Keywords checked: 5
AI Overviews present: 4/5 (80%)
Brand cited in AO: 2/5 (40%)
Avg organic position: 7.0
Cost: $0.025