Overview
This workflow runs every Monday to scan target keywords for AI Overview citations, tracking whether your domain and competitor domains are cited in Google's AI-generated answers. It stores weekly snapshots, computes week-over-week citation changes, and flags new citation wins and losses. The output feeds into GEO strategy meetings with concrete data on which content updates improved citation rates.
Trigger
Cron schedule (every Monday at 6:00 AM UTC)
Schedule
Runs every Monday at 6:00 AM UTC
Workflow Steps
Load keyword and domain lists
Read target keywords and tracked domains (yours + competitors) from configuration.
Query keywords with AI Overview
Search Scavio Google with ai_overview enabled for each keyword to get current citation data.
Extract citation sources
Parse AI Overview citations and check which tracked domains appear in each.
Compare against previous week
Load last week's snapshot and compute citation wins, losses, and unchanged positions.
Generate weekly GEO report
Output a structured report with citation rates, trends, and actionable recommendations.
Python Implementation
import requests
import json
from datetime import datetime
from pathlib import Path
API_KEY = "your_scavio_api_key"
def scan_citations(keywords: list[str], domains: list[str]) -> dict:
results = []
for kw in keywords:
res = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY},
json={"platform": "google", "query": kw, "ai_overview": True},
timeout=15,
)
res.raise_for_status()
data = res.json()
ai = data.get("ai_overview")
citations = [c.get("source", "") for c in (ai or {}).get("citations", [])]
domain_status = {}
for domain in domains:
domain_status[domain] = any(domain in c for c in citations)
results.append({"keyword": kw, "has_ai_overview": bool(ai), "citations": domain_status})
return results
def run():
date = datetime.utcnow().strftime("%Y-%m-%d")
keywords = ["best search api", "serp api pricing", "web scraping api", "ai search tool"]
domains = ["scavio.dev", "serpapi.com", "tavily.com"]
results = scan_citations(keywords, domains)
for domain in domains:
cited = sum(1 for r in results if r["citations"].get(domain))
print(f" {domain}: {cited}/{len(keywords)} citations ({cited/len(keywords)*100:.0f}%)")
snapshot = {"date": date, "keywords_scanned": len(keywords), "domains": domains, "results": results}
Path(f"geo_scan_{date}.json").write_text(json.dumps(snapshot, indent=2))
print(f"GEO scan {date}: {len(keywords)} keywords, {len(domains)} domains tracked")
if __name__ == "__main__":
run()JavaScript Implementation
const API_KEY = "your_scavio_api_key";
async function scanCitations(keywords, domains) {
const results = [];
for (const kw of keywords) {
const res = await fetch("https://api.scavio.dev/api/v1/search", {
method: "POST",
headers: { "x-api-key": API_KEY, "content-type": "application/json" },
body: JSON.stringify({ platform: "google", query: kw, ai_overview: true }),
});
const data = await res.json();
const citations = (data.ai_overview?.citations ?? []).map((c) => c.source ?? "");
const status = {};
for (const d of domains) status[d] = citations.some((c) => c.includes(d));
results.push({ keyword: kw, citations: status });
}
return results;
}
const results = await scanCitations(["best search api", "serp api pricing"], ["scavio.dev", "serpapi.com"]);
for (const r of results) console.log(`${r.keyword}: ${JSON.stringify(r.citations)}`);Platforms Used
Web search with knowledge graph, PAA, and AI overviews