Overview
Generative Engine Optimization (GEO) is becoming critical as AI Overviews dominate SERPs. This workflow runs every Monday, takes your top-performing pages, checks whether they appear in AI Overviews, and flags compliance gaps such as missing structured data, weak citations, or absent entity markup. At $0.005 per credit, auditing 50 pages costs about $0.25 per week. Results are exported as a prioritized fix list so your content team knows exactly what to update.
Trigger
Cron Monday 6 AM UTC
Schedule
Weekly Monday 6 AM
Workflow Steps
Load Page Inventory
Read the list of top pages and their target keywords from a CSV or database.
Search Each Keyword via Scavio
For each keyword, call Scavio search on Google to capture the full SERP including AI Overview data.
Detect AI Overview Presence
Parse the response to check whether your URL appears in the AI Overview section or only in traditional organic results.
Score GEO Compliance
Evaluate each page against GEO signals: structured data, citation density, entity mentions, and authoritative sourcing.
Generate Fix List
Rank pages by compliance gap severity and export a prioritized spreadsheet with specific recommended fixes.
Python Implementation
import requests, os, json, csv
from pathlib import Path
from datetime import date
API_KEY = os.environ["SCAVIO_API_KEY"]
SH = {"x-api-key": API_KEY, "Content-Type": "application/json"}
PAGES_FILE = Path("top_pages.csv")
def search_keyword(keyword: str) -> dict:
resp = requests.post(
"https://api.scavio.dev/api/v1/search",
headers=SH,
json={"query": keyword, "platform": "google"},
timeout=15,
)
resp.raise_for_status()
return resp.json()
def check_ai_overview(serp: dict, target_url: str) -> dict:
ai_overview = serp.get("ai_overview", {})
in_overview = False
if ai_overview:
sources = ai_overview.get("sources", [])
in_overview = any(target_url in s.get("url", "") for s in sources)
organic = serp.get("organic", [])
organic_pos = next((i + 1 for i, r in enumerate(organic) if target_url in r.get("url", "")), None)
return {"in_ai_overview": in_overview, "organic_position": organic_pos}
def run_audit():
results = []
with open(PAGES_FILE) as f:
reader = csv.DictReader(f)
for row in reader:
keyword = row["keyword"]
url = row["url"]
serp = search_keyword(keyword)
presence = check_ai_overview(serp, url)
score = 100
if not presence["in_ai_overview"]:
score -= 40
if presence["organic_position"] is None:
score -= 30
elif presence["organic_position"] > 5:
score -= 15
results.append({
"keyword": keyword,
"url": url,
"geo_score": score,
**presence,
})
results.sort(key=lambda r: r["geo_score"])
out = Path(f"geo_audit_{date.today()}.json")
out.write_text(json.dumps(results, indent=2))
print(f"Audit complete: {len(results)} pages checked")
for r in results[:10]:
print(f" [{r['geo_score']}] {r['keyword']} - AI Overview: {r['in_ai_overview']}")
return results
run_audit()JavaScript Implementation
const SH = {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'};
const fs = await import('fs');
const pages = fs.readFileSync('top_pages.csv', 'utf8').trim().split('\n');
const header = pages.shift().split(',');
const rows = pages.map(line => {
const cols = line.split(',');
return {keyword: cols[header.indexOf('keyword')], url: cols[header.indexOf('url')]};
});
async function searchKeyword(keyword) {
const r = await fetch('https://api.scavio.dev/api/v1/search', {method:'POST', headers:SH, body:JSON.stringify({query:keyword, platform:'google'})});
return r.json();
}
function checkAiOverview(serp, targetUrl) {
const aiOverview = serp.ai_overview || {};
const inOverview = (aiOverview.sources || []).some(s => (s.url || '').includes(targetUrl));
const organicPos = (serp.organic || []).findIndex(r => (r.url || '').includes(targetUrl));
return {inAiOverview: inOverview, organicPosition: organicPos >= 0 ? organicPos + 1 : null};
}
const results = [];
for (const row of rows) {
const serp = await searchKeyword(row.keyword);
const presence = checkAiOverview(serp, row.url);
let score = 100;
if (!presence.inAiOverview) score -= 40;
if (presence.organicPosition === null) score -= 30;
else if (presence.organicPosition > 5) score -= 15;
results.push({keyword:row.keyword, url:row.url, geoScore:score, ...presence});
}
results.sort((a,b) => a.geoScore - b.geoScore);
fs.writeFileSync('geo_audit_'+new Date().toISOString().split('T')[0]+'.json', JSON.stringify(results, null, 2));
console.log('Audit complete: '+results.length+' pages');
results.slice(0,10).forEach(r => console.log(' ['+r.geoScore+'] '+r.keyword+' - AI Overview: '+r.inAiOverview));Platforms Used
Web search with knowledge graph, PAA, and AI overviews