Overview
This workflow pulls fresh SERP data weekly for all tracked keywords and feeds it into your custom dashboard. It tracks organic positions, SERP feature presence, competitor movements, and AI Overview changes. The output updates your dashboard database or generates a JSON report for visualization tools.
Trigger
Cron schedule (every Monday at 6:00 AM UTC)
Schedule
Runs every Monday at 6:00 AM UTC
Workflow Steps
Load keyword list from config
Read the tracked keywords and target domain from configuration file or database.
Query SERP for each keyword
Call Scavio for each keyword with AI Overview enabled and num set to 20 for deeper position tracking.
Extract position and feature data
Parse each response for target domain position, SERP feature presence, and top competitor domains.
Compare against previous week
Load last week's data and compute position changes, new/lost features, and competitor movements.
Update dashboard database
Write current week data to dashboard storage and generate change alerts for significant movements.
Python Implementation
import requests
import json
from pathlib import Path
from datetime import datetime
API_KEY = "your_scavio_api_key"
DOMAIN = "yourdomain.com"
KEYWORDS = ["best search API", "SERP API pricing", "web scraping alternative", "search data for AI agents"]
def query_serp(keyword: str) -> dict:
res = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY},
json={"platform": "google", "query": keyword, "num": 20, "ai_overview": True},
timeout=15,
)
res.raise_for_status()
data = res.json()
position = None
for item in data.get("organic", []):
if DOMAIN in item.get("link", ""):
position = item.get("position")
break
return {
"keyword": keyword,
"position": position,
"has_ai_overview": bool(data.get("ai_overview")),
"has_featured_snippet": bool(data.get("featured_snippet")),
"top_3_domains": [r.get("link", "").split("/")[2] if "/" in r.get("link", "") else "" for r in data.get("organic", [])[:3]],
}
def run():
date = datetime.utcnow().strftime("%Y-%m-%d")
history_path = Path("dashboard_history.json")
history = json.loads(history_path.read_text()) if history_path.exists() else {}
current = {}
alerts = []
for kw in KEYWORDS:
result = query_serp(kw)
current[kw] = result
prev = history.get(kw, {})
if prev.get("position") and result["position"]:
change = result["position"] - prev["position"]
if abs(change) >= 3:
alerts.append({"keyword": kw, "previous": prev["position"], "current": result["position"], "change": change})
history_path.write_text(json.dumps(current, indent=2))
report = {
"date": date,
"keywords_tracked": len(KEYWORDS),
"keywords_ranking": sum(1 for r in current.values() if r["position"]),
"alerts": alerts,
"data": current,
}
Path(f"dashboard_{date}.json").write_text(json.dumps(report, indent=2))
print(f"Dashboard refresh: {report['keywords_ranking']}/{report['keywords_tracked']} ranking")
for alert in alerts:
direction = "dropped" if alert["change"] > 0 else "improved"
print(f" ALERT: {alert['keyword']} {direction} by {abs(alert['change'])} (#{alert['previous']} -> #{alert['current']})")
if __name__ == "__main__":
run()JavaScript Implementation
const API_KEY = "your_scavio_api_key";
const DOMAIN = "yourdomain.com";
const KEYWORDS = ["best search API", "SERP API pricing", "web scraping alternative"];
async function querySerp(keyword) {
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: keyword, num: 20, ai_overview: true }),
});
if (!res.ok) throw new Error(`scavio ${res.status}`);
const data = await res.json();
let position = null;
for (const item of data.organic ?? []) {
if (item.link?.includes(DOMAIN)) { position = item.position; break; }
}
return { keyword, position, hasAiOverview: !!data.ai_overview, hasFeaturedSnippet: !!data.featured_snippet };
}
async function run() {
const fs = await import("fs/promises");
let history = {};
try { history = JSON.parse(await fs.readFile("dashboard_history.json", "utf8")); } catch {}
const current = {};
const alerts = [];
for (const kw of KEYWORDS) {
const result = await querySerp(kw);
current[kw] = result;
const prev = history[kw] ?? {};
if (prev.position && result.position) {
const change = result.position - prev.position;
if (Math.abs(change) >= 3) alerts.push({ keyword: kw, previous: prev.position, current: result.position, change });
}
}
await fs.writeFile("dashboard_history.json", JSON.stringify(current, null, 2));
const ranking = Object.values(current).filter((r) => r.position).length;
console.log(`Dashboard: ${ranking}/${KEYWORDS.length} ranking, ${alerts.length} alerts`);
for (const a of alerts) console.log(` ${a.keyword}: #${a.previous} -> #${a.current}`);
}
run();Platforms Used
Web search with knowledge graph, PAA, and AI overviews