Tracking competitor search rankings manually is unsustainable. This tutorial builds an automated tracker that queries target keywords daily via Scavio's Google endpoint, logs competitor positions, and flags significant ranking changes. It costs a fraction of enterprise SERP tools because you pay per query with credit-based pricing instead of flat monthly fees.
Prerequisites
- Python 3.8+ installed
- requests library installed
- A Scavio API key from scavio.dev
- A list of target keywords and competitor domains
Walkthrough
Step 1: Define keywords and competitors
Set up your tracking configuration with target keywords and competitor domains.
import os
API_KEY = os.environ['SCAVIO_API_KEY']
KEYWORDS = ['best crm 2026', 'project management software', 'invoice tool small business']
COMPETITORS = ['competitor1.com', 'competitor2.com', 'competitor3.com']
MY_DOMAIN = 'mydomain.com'Step 2: Query rankings for each keyword
Search each keyword and extract positions for your domain and competitors.
import requests
def get_rankings(keyword: str) -> list:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'platform': 'google', 'query': keyword}, timeout=10)
rankings = []
for i, r in enumerate(resp.json().get('organic', [])[:20], 1):
domain = r.get('link', '').split('/')[2] if '//' in r.get('link', '') else ''
if domain == MY_DOMAIN or any(c in domain for c in COMPETITORS):
rankings.append({'position': i, 'domain': domain, 'title': r['title']})
return rankingsStep 3: Compare with previous day
Load yesterday's data and flag position changes above a threshold.
import json, datetime
def compare_rankings(today: dict, yesterday: dict, threshold: int = 3) -> list:
alerts = []
for kw in today:
for entry in today[kw]:
prev = next((e for e in yesterday.get(kw, []) if e['domain'] == entry['domain']), None)
if prev:
change = prev['position'] - entry['position']
if abs(change) >= threshold:
direction = 'up' if change > 0 else 'down'
alerts.append(f"{entry['domain']} moved {direction} {abs(change)} positions for '{kw}' (now #{entry['position']})")
else:
alerts.append(f"NEW: {entry['domain']} entered top 20 for '{kw}' at #{entry['position']}")
return alertsStep 4: Run the daily tracker
Combine everything into a daily run function that saves results and prints alerts.
def daily_track():
today_data = {}
for kw in KEYWORDS:
today_data[kw] = get_rankings(kw)
date = datetime.date.today().isoformat()
filename = f'serp_rankings_{date}.json'
with open(filename, 'w') as f:
json.dump(today_data, f, indent=2)
yesterday_file = f'serp_rankings_{(datetime.date.today() - datetime.timedelta(1)).isoformat()}.json'
try:
with open(yesterday_file) as f:
yesterday_data = json.load(f)
alerts = compare_rankings(today_data, yesterday_data)
for a in alerts: print(f'ALERT: {a}')
except FileNotFoundError:
print('First run, no comparison data yet.')
daily_track()Python Example
# Run as a cron job: 0 8 * * * python serp_tracker.py
import requests, os, json, datetime
H = {'x-api-key': os.environ['SCAVIO_API_KEY']}
def track(keywords, competitors):
for kw in keywords:
data = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'platform': 'google', 'query': kw}, timeout=10).json()
for i, r in enumerate(data.get('organic', [])[:20], 1):
domain = r.get('link', '').split('/')[2] if '//' in r.get('link', '') else ''
if any(c in domain for c in competitors):
print(f'[{i:2d}] {domain:30s} | {kw}')JavaScript Example
async function track(keywords, competitors) {
for (const kw of keywords) {
const data = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'},
body: JSON.stringify({platform: 'google', query: kw})
}).then(r => r.json());
(data.organic || []).forEach((r, i) => {
const domain = new URL(r.link).hostname;
if (competitors.some(c => domain.includes(c))) console.log(`[${i+1}] ${domain} | ${kw}`);
});
}
}Expected Output
Daily SERP ranking reports with position change alerts. Tracks competitor and own-domain positions for target keywords.