ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. How to Monitor Competitor SERP Positions with an API
Tutorial

How to Monitor Competitor SERP Positions with an API

Build a daily SERP rank tracker that stores competitor positions in SQLite and sends alerts when rankings change. Uses the Scavio search API.

Get Free API KeyAPI Docs

You can monitor competitor SERP positions by searching target keywords daily via API, extracting the domain position from organic results, storing it in SQLite, and alerting when a competitor's rank changes by more than a threshold.

Prerequisites

  • Python 3.9+
  • Scavio API key
  • SQLite (stdlib)
  • cron or a scheduler

Walkthrough

Step 1: Set up the tracking database

Create a SQLite table to store daily position snapshots.

Python
import sqlite3

def init_db(path: str = "serp_tracker.db") -> sqlite3.Connection:
    conn = sqlite3.connect(path)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS positions (
            date TEXT,
            keyword TEXT,
            domain TEXT,
            position INTEGER,
            PRIMARY KEY (date, keyword, domain)
        )
    """)
    conn.commit()
    return conn

Step 2: Extract domain positions from search results

Search the keyword and find each tracked domain in the organic results.

Python
import requests
from datetime import date

API_KEY = "your-scavio-api-key"

def get_positions(keyword: str, domains: list[str]) -> dict:
    r = requests.post(
        "https://api.scavio.dev/api/v1/search",
        json={"query": keyword, "num_results": 20},
        headers={"x-api-key": API_KEY},
        timeout=15
    )
    r.raise_for_status()
    results = r.json().get("organic_results", [])
    positions = {}
    for domain in domains:
        pos = next(
            (i + 1 for i, res in enumerate(results) if domain in res.get("link", "")),
            None
        )
        positions[domain] = pos
    return positions

Step 3: Store and compare positions

Save today's positions and compare with yesterday's to detect changes.

Python
def store_positions(conn, keyword: str, positions: dict):
    today = str(date.today())
    for domain, pos in positions.items():
        conn.execute(
            "INSERT OR REPLACE INTO positions VALUES (?, ?, ?, ?)",
            (today, keyword, domain, pos)
        )
    conn.commit()

def get_yesterday_positions(conn, keyword: str, domains: list) -> dict:
    rows = conn.execute(
        "SELECT domain, position FROM positions WHERE keyword=? ORDER BY date DESC LIMIT ?",
        (keyword, len(domains) * 2)
    ).fetchall()
    # Get second-to-last date for comparison
    dates = conn.execute(
        "SELECT DISTINCT date FROM positions WHERE keyword=? ORDER BY date DESC LIMIT 2",
        (keyword,)
    ).fetchall()
    if len(dates) < 2:
        return {}
    prev_date = dates[1][0]
    rows = conn.execute(
        "SELECT domain, position FROM positions WHERE keyword=? AND date=?",
        (keyword, prev_date)
    ).fetchall()
    return dict(rows)

Step 4: Alert on rank changes

Print (or send to Slack/email) when a competitor gains or loses 3+ positions.

Python
def check_alerts(keyword: str, today_pos: dict, yesterday_pos: dict, threshold: int = 3):
    alerts = []
    for domain, pos in today_pos.items():
        prev = yesterday_pos.get(domain)
        if prev is None or pos is None:
            continue
        change = prev - pos  # positive = rank improved (moved up)
        if abs(change) >= threshold:
            direction = "up" if change > 0 else "down"
            alerts.append(f"{domain} moved {direction} {abs(change)} positions for '{keyword}': {prev} -> {pos}")
    return alerts

# Example run
conn = init_db()
KEYWORDS = ["project management software", "task tracker online"]
DOMAINS = ["asana.com", "monday.com", "notion.so", "clickup.com"]

for kw in KEYWORDS:
    positions = get_positions(kw, DOMAINS)
    yesterday = get_yesterday_positions(conn, kw, DOMAINS)
    store_positions(conn, kw, positions)
    alerts = check_alerts(kw, positions, yesterday)
    for alert in alerts:
        print("ALERT:", alert)

Python Example

Python
import requests
import sqlite3
from datetime import date

API_KEY = "your-scavio-api-key"
KEYWORDS = ["project management software", "task tracker for teams", "kanban board online"]
DOMAINS = ["asana.com", "monday.com", "notion.so", "clickup.com", "linear.app"]

def init_db(path="serp_tracker.db"):
    conn = sqlite3.connect(path)
    conn.execute("CREATE TABLE IF NOT EXISTS positions (date TEXT, keyword TEXT, domain TEXT, position INTEGER, PRIMARY KEY (date, keyword, domain))")
    conn.commit()
    return conn

def get_positions(keyword, domains):
    r = requests.post("https://api.scavio.dev/api/v1/search",
                      json={"query": keyword, "num_results": 20},
                      headers={"x-api-key": API_KEY}, timeout=15)
    r.raise_for_status()
    results = r.json().get("organic_results", [])
    return {d: next((i+1 for i, res in enumerate(results) if d in res.get("link","")), None) for d in domains}

def store(conn, keyword, positions):
    today = str(date.today())
    for domain, pos in positions.items():
        conn.execute("INSERT OR REPLACE INTO positions VALUES (?,?,?,?)", (today, keyword, domain, pos))
    conn.commit()

def prev_positions(conn, keyword):
    dates = conn.execute("SELECT DISTINCT date FROM positions WHERE keyword=? ORDER BY date DESC LIMIT 2", (keyword,)).fetchall()
    if len(dates) < 2:
        return {}
    rows = conn.execute("SELECT domain, position FROM positions WHERE keyword=? AND date=?", (keyword, dates[1][0])).fetchall()
    return dict(rows)

def run_tracker():
    conn = init_db()
    for kw in KEYWORDS:
        today_pos = get_positions(kw, DOMAINS)
        yesterday_pos = prev_positions(conn, kw)
        store(conn, kw, today_pos)
        print(f"\nKeyword: {kw}")
        for domain in DOMAINS:
            pos = today_pos.get(domain)
            prev = yesterday_pos.get(domain)
            change = f" (was {prev})" if prev and prev != pos else ""
            print(f"  {domain}: {pos or 'not ranking'}{change}")
    conn.close()

if __name__ == "__main__":
    run_tracker()

JavaScript Example

JavaScript
const API_KEY = 'your-scavio-api-key';
const KEYWORDS = ['project management software', 'task tracker for teams'];
const DOMAINS = ['asana.com', 'monday.com', 'notion.so', 'clickup.com'];

async function getPositions(keyword, domains) {
  const res = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'x-api-key': API_KEY },
    body: JSON.stringify({ query: keyword, num_results: 20 })
  });
  const data = await res.json();
  const results = data.organic_results ?? [];
  return Object.fromEntries(domains.map(d => [
    d,
    (results.findIndex(r => r.link?.includes(d)) + 1) || null
  ]));
}

for (const kw of KEYWORDS) {
  const positions = await getPositions(kw, DOMAINS);
  console.log(`\nKeyword: ${kw}`);
  for (const [domain, pos] of Object.entries(positions)) {
    console.log(`  ${domain}: ${pos ?? 'not ranking'}`);
  }
}

Expected Output

JSON
Keyword: project management software
  asana.com: 2
  monday.com: 4 (was 6)
  notion.so: 7
  clickup.com: 11
  linear.app: not ranking

ALERT: monday.com moved up 2 positions for 'project management software': 6 -> 4

Related Tutorials

  • How to Build an SEO Keyword Gap Finder
  • How to Build an SEO Audit Agent in Claude Code
  • How to Track Products in Google AI Overview Results

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.9+. Scavio API key. SQLite (stdlib). cron or a scheduler. A Scavio API key gives you 50 free credits on signup.

Yes. The free tier includes 50 credits on signup, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Related Resources

Best Of

Best SEO Rank Tracking APIs in 2026

Read more
Best Of

Best API-Based Rank Trackers for SEO in 2026

Read more
Solution

Automated Competitor SERP Tracking

Read more
Use Case

SEO Light User API Workflow

Read more
Workflow

Daily Local Rank Tracking Pipeline

Read more
Solution

Build Reliable Local Rank Tracking with Scavio API

Read more

Start Building

Build a daily SERP rank tracker that stores competitor positions in SQLite and sends alerts when rankings change. Uses the Scavio search API.

Get Free API KeyRead the Docs
ScavioScavio

Real-time search API for AI agents. Search every platform, not just Google.

Product

  • Features
  • Pricing
  • Dashboard
  • Affiliates

Developers

  • Documentation
  • API Reference
  • Quickstart
  • MCP Integration
  • Python SDK

Alternatives

  • Tavily Alternative
  • SerpAPI Alternative
  • Firecrawl Alternative
  • Exa Alternative

Tools

  • JSON Formatter
  • cURL to Code
  • Token Counter
  • All Tools

© 2026 Scavio. All rights reserved.

Featured on TAAFT
Terms of ServicePrivacy Policy