Overview
Most rank trackers charge per keyword per month. At 4,000 keywords, bills exceed $200/month for data that teams only review weekly. This workflow runs a batch rank check every Sunday night, stores results in a database, and generates a diff report comparing this week to last week. Total weekly cost: $20.
Trigger
Cron schedule: every Sunday at 11 PM UTC.
Schedule
Weekly (Sunday 11 PM UTC)
Workflow Steps
Load Keyword List from Database
Read all keyword-location pairs from the keywords table. Each row has keyword text, target country, and the tracked domain.
Batch Query All Keywords
Loop through all 4,000 keywords with a 200ms delay between requests. For each keyword, fetch the top 10 organic results and extract the target domain position.
Store Results with Timestamp
Write each result to the rankings table with the current date. Include position, URL, and title for the target domain and top 3 competitors.
Generate Weekly Diff Report
Compare this week's positions against last week. Flag keywords that gained or lost 3+ positions. Summarize movers in a report.
Send Report to Slack or Email
Push the weekly diff report to Slack or email. Include top gainers, top losers, and newly entered/dropped keywords.
Python Implementation
import requests, os, json, time
from datetime import date
API_KEY = os.environ["SCAVIO_API_KEY"]
KEYWORDS = json.load(open("keywords.json")) # [{"kw":"...","cc":"us","domain":"example.com"}, ...]
def check_rank(kw, cc, target_domain):
resp = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
json={"query": kw, "country_code": cc},
timeout=15,
)
data = resp.json()
position = None
for r in data.get("organic_results", []):
if target_domain in r.get("link", ""):
position = r.get("position")
break
return {"keyword": kw, "country": cc, "date": str(date.today()), "position": position}
results = []
for item in KEYWORDS:
results.append(check_rank(item["kw"], item["cc"], item["domain"]))
time.sleep(0.2)
with open(f"ranks_{date.today()}.json", "w") as f:
json.dump(results, f, indent=2)
print(f"Checked {len(results)} keywords, {sum(1 for r in results if r['position'])} found")JavaScript Implementation
const fs = require('fs');
const KEYWORDS = JSON.parse(fs.readFileSync('keywords.json','utf8'));
const H = {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'};
async function checkRank(kw, cc, domain) {
const r = await fetch('https://api.scavio.dev/api/v1/search', {method:'POST', headers:H, body:JSON.stringify({query:kw, country_code:cc})});
const d = await r.json();
const match = (d.organic_results||[]).find(r=>(r.link||'').includes(domain));
return {keyword:kw, country:cc, date:new Date().toISOString().slice(0,10), position:match?.position||null};
}
const results = [];
for (const k of KEYWORDS) { results.push(await checkRank(k.kw, k.cc, k.domain)); await new Promise(r=>setTimeout(r,200)); }
fs.writeFileSync('ranks_'+new Date().toISOString().slice(0,10)+'.json', JSON.stringify(results,null,2));
console.log(results.length+' keywords checked');Platforms Used
Web search with knowledge graph, PAA, and AI overviews