Overview
This workflow goes beyond rank tracking by monitoring the entire SERP structure for a set of keywords. It detects when featured snippets appear or disappear, when People Also Ask boxes change, when new SERP features like video carousels or knowledge panels show up, and when organic positions shift. SEO teams use it to understand the full competitive landscape, not just position numbers.
Trigger
Cron schedule (daily at 6 AM UTC)
Schedule
Runs daily at 6 AM UTC
Workflow Steps
Load keyword set
Read the target keywords and the SERP features to monitor for each.
Fetch full SERP data
Call the Scavio API with platform google for each keyword, requesting all available SERP features.
Extract SERP structure
Parse the response for organic positions, featured snippets, People Also Ask, knowledge panels, and video carousels.
Diff against previous snapshot
Compare the current SERP structure to the previous day's snapshot. Identify what appeared, disappeared, or moved.
Classify changes
Tag each change as a ranking shift, feature appearance, feature removal, or content change within a feature.
Generate report
Compile all changes into a structured report and deliver via email or Slack.
Python Implementation
import requests
import json
from pathlib import Path
from datetime import datetime
API_KEY = "your_scavio_api_key"
def fetch_serp(query: str) -> dict:
res = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY},
json={
"platform": "google",
"query": query,
"num": 20,
"ai_overview": True,
},
timeout=15,
)
res.raise_for_status()
data = res.json()
return {
"top_3": [r.get("link", "") for r in data.get("organic", [])[:3]],
"has_featured_snippet": data.get("featured_snippet") is not None,
"has_ai_overview": data.get("ai_overview") is not None,
"paa_count": len(data.get("people_also_ask", [])),
"has_knowledge_panel": data.get("knowledge_graph") is not None,
}
def run():
keywords = [
"best SERP API 2026",
"Google search API alternative",
"web scraping vs API",
]
snapshot_path = Path("serp_snapshots.json")
previous = json.loads(snapshot_path.read_text()) if snapshot_path.exists() else {}
changes = []
current = {}
for kw in keywords:
serp = fetch_serp(kw)
current[kw] = serp
prev = previous.get(kw)
if prev:
diffs = []
if prev["top_3"] != serp["top_3"]:
diffs.append("top 3 organic positions changed")
if prev["has_featured_snippet"] != serp["has_featured_snippet"]:
status = "appeared" if serp["has_featured_snippet"] else "disappeared"
diffs.append(f"featured snippet {status}")
if prev["has_ai_overview"] != serp["has_ai_overview"]:
status = "appeared" if serp["has_ai_overview"] else "disappeared"
diffs.append(f"AI Overview {status}")
if diffs:
changes.append({"keyword": kw, "changes": diffs})
snapshot_path.write_text(json.dumps(current, indent=2))
date_str = datetime.utcnow().strftime("%Y-%m-%d")
if changes:
print(f"SERP changes detected on {date_str}:")
for c in changes:
print(f" {c['keyword']}: {', '.join(c['changes'])}")
else:
print(f"No SERP changes on {date_str}")
if __name__ == "__main__":
run()JavaScript Implementation
const API_KEY = "your_scavio_api_key";
async function fetchSerp(query) {
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,
num: 20,
ai_overview: true,
}),
});
if (!res.ok) throw new Error(`scavio ${res.status}`);
const data = await res.json();
return {
top3: (data.organic ?? []).slice(0, 3).map((r) => r.link ?? ""),
hasFeaturedSnippet: data.featured_snippet != null,
hasAiOverview: data.ai_overview != null,
paaCount: (data.people_also_ask ?? []).length,
hasKnowledgePanel: data.knowledge_graph != null,
};
}
async function run() {
const fs = await import("fs/promises");
const keywords = [
"best SERP API 2026",
"Google search API alternative",
"web scraping vs API",
];
let previous = {};
try {
previous = JSON.parse(await fs.readFile("serp_snapshots.json", "utf8"));
} catch {}
const current = {};
const changes = [];
for (const kw of keywords) {
const serp = await fetchSerp(kw);
current[kw] = serp;
const prev = previous[kw];
if (prev) {
const diffs = [];
if (JSON.stringify(prev.top3) !== JSON.stringify(serp.top3)) {
diffs.push("top 3 organic positions changed");
}
if (prev.hasFeaturedSnippet !== serp.hasFeaturedSnippet) {
diffs.push(`featured snippet ${serp.hasFeaturedSnippet ? "appeared" : "disappeared"}`);
}
if (prev.hasAiOverview !== serp.hasAiOverview) {
diffs.push(`AI Overview ${serp.hasAiOverview ? "appeared" : "disappeared"}`);
}
if (diffs.length) changes.push({ keyword: kw, changes: diffs });
}
}
await fs.writeFile("serp_snapshots.json", JSON.stringify(current, null, 2));
const date = new Date().toISOString().slice(0, 10);
if (changes.length) {
console.log(`SERP changes detected on ${date}:`);
for (const c of changes) {
console.log(` ${c.keyword}: ${c.changes.join(", ")}`);
}
} else {
console.log(`No SERP changes on ${date}`);
}
}
run();Platforms Used
Web search with knowledge graph, PAA, and AI overviews