Tutorial

How to Build a Budget Rank Tracker with the Scavio Search API

Track 50 keywords daily for $0.035/week using the Scavio Search API. Store rank history in SQLite and get alerts when positions shift by 3+ spots.

Enterprise rank trackers charge $50-200/month to monitor a handful of keywords. With the Scavio Search API at $0.005/credit, you can track 50 keywords daily for $0.035/week -- under $2/year. This tutorial builds a complete rank tracker with SQLite storage, daily cron scheduling, and position change alerts.

Prerequisites

  • Python 3.11+
  • A Scavio API key from https://scavio.dev
  • SQLite3 (included with Python)
  • A cron scheduler or Task Scheduler for daily runs

Walkthrough

Step 1: Set up the SQLite database and keyword list

Create a SQLite database to store daily rank snapshots. Each row records the keyword, the target domain's position, the date, and the URL that ranked.

Python
import sqlite3
from pathlib import Path

DB_PATH = Path("rank_tracker.db")

def init_db():
    conn = sqlite3.connect(DB_PATH)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS rankings (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            keyword TEXT NOT NULL,
            position INTEGER,
            url TEXT,
            checked_at TEXT NOT NULL,
            domain TEXT NOT NULL
        )
    """)
    conn.execute("""
        CREATE INDEX IF NOT EXISTS idx_keyword_date
        ON rankings(keyword, checked_at)
    """)
    conn.commit()
    conn.close()

KEYWORDS = [
    "best API for web scraping 2026",
    "cheap search API for agents",
    "MCP server search tool",
    # ... add up to 50 keywords
]

TARGET_DOMAIN = "scavio.dev"
init_db()

Step 2: Check rankings via the Scavio Search API

For each keyword, search and find your target domain's position in the results. Store the position (or null if not found in top results) in the database.

Python
import httpx
from datetime import date

SCAVIO_API_KEY = "your-api-key"

async def check_ranking(client: httpx.AsyncClient, keyword: str) -> dict:
    resp = await client.post(
        "https://api.scavio.dev/api/v1/search",
        headers={"x-api-key": SCAVIO_API_KEY},
        json={"query": keyword, "num_results": 10}
    )
    resp.raise_for_status()
    results = resp.json().get("results", [])
    for i, r in enumerate(results):
        url = r.get("url", "")
        if TARGET_DOMAIN in url:
            return {"position": i + 1, "url": url}
    return {"position": None, "url": None}

async def check_all_keywords():
    conn = sqlite3.connect(DB_PATH)
    today = date.today().isoformat()
    async with httpx.AsyncClient(timeout=15) as client:
        for keyword in KEYWORDS:
            result = await check_ranking(client, keyword)
            conn.execute(
                "INSERT INTO rankings (keyword, position, url, checked_at, domain) VALUES (?, ?, ?, ?, ?)",
                (keyword, result["position"], result["url"], today, TARGET_DOMAIN)
            )
    conn.commit()
    conn.close()

Step 3: Detect position changes and generate alerts

Compare today's rankings against yesterday's. Flag any keyword that moved 3 or more positions in either direction.

Python
def get_position_changes(threshold: int = 3) -> list[dict]:
    conn = sqlite3.connect(DB_PATH)
    rows = conn.execute("""
        SELECT
            r1.keyword,
            r2.position AS prev_position,
            r1.position AS curr_position,
            r1.url,
            r1.checked_at
        FROM rankings r1
        JOIN rankings r2
            ON r1.keyword = r2.keyword
            AND r1.domain = r2.domain
            AND r2.checked_at = date(r1.checked_at, '-1 day')
        WHERE r1.checked_at = date('now')
            AND r1.position IS NOT NULL
            AND r2.position IS NOT NULL
            AND ABS(r1.position - r2.position) >= ?
        ORDER BY ABS(r1.position - r2.position) DESC
    """, (threshold,)).fetchall()
    conn.close()
    changes = []
    for keyword, prev, curr, url, checked in rows:
        direction = "up" if curr < prev else "down"
        changes.append({
            "keyword": keyword,
            "previous": prev,
            "current": curr,
            "change": prev - curr,
            "direction": direction,
            "url": url
        })
    return changes

Step 4: Generate a daily cost and summary report

Print a summary showing total keywords tracked, credits used, cost, and any significant position changes. Schedule this script with cron for daily automation.

Python
import asyncio

async def daily_report():
    await check_all_keywords()

    credits_used = len(KEYWORDS)  # 1 credit per search
    cost = credits_used * 0.005
    weekly_cost = cost * 7

    changes = get_position_changes(threshold=3)

    print(f"Rank Tracker Report - {date.today().isoformat()}")
    print(f"Keywords tracked: {len(KEYWORDS)}")
    print(f"Credits used: {credits_used}")
    print(f"Daily cost: {cost:.3f}")
    print(f"Weekly cost: {weekly_cost:.3f}")
    print(f"Position changes (3+): {len(changes)}")
    for c in changes:
        arrow = "improved" if c["direction"] == "up" else "dropped"
        print(f"  {c['keyword']}: #{c['previous']} -> #{c['current']} ({arrow} by {abs(c['change'])})")

asyncio.run(daily_report())

# Cron: 0 8 * * * cd /path/to/tracker && python rank_tracker.py

Python Example

Python
import asyncio
import sqlite3
import httpx
from datetime import date
from pathlib import Path

SCAVIO_API_KEY = "your-api-key"
TARGET_DOMAIN = "scavio.dev"
DB_PATH = Path("rank_tracker.db")

KEYWORDS = [
    "best API for web scraping 2026",
    "cheap search API for agents",
    "MCP server search tool",
]

def init_db():
    conn = sqlite3.connect(DB_PATH)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS rankings (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            keyword TEXT, position INTEGER, url TEXT,
            checked_at TEXT, domain TEXT
        )
    """)
    conn.commit()
    conn.close()

async def check_ranking(client, keyword):
    resp = await client.post(
        "https://api.scavio.dev/api/v1/search",
        headers={"x-api-key": SCAVIO_API_KEY},
        json={"query": keyword, "num_results": 10}
    )
    resp.raise_for_status()
    for i, r in enumerate(resp.json().get("results", [])):
        if TARGET_DOMAIN in r.get("url", ""):
            return i + 1, r["url"]
    return None, None

async def main():
    init_db()
    conn = sqlite3.connect(DB_PATH)
    today = date.today().isoformat()
    async with httpx.AsyncClient(timeout=15) as client:
        for kw in KEYWORDS:
            pos, url = await check_ranking(client, kw)
            conn.execute(
                "INSERT INTO rankings VALUES (NULL,?,?,?,?,?)",
                (kw, pos, url, today, TARGET_DOMAIN)
            )
    conn.commit()
    cost = len(KEYWORDS) * 0.005
    print(f"Tracked {len(KEYWORDS)} keywords | Cost: {cost:.3f} | Weekly: {cost * 7:.3f}")
    conn.close()

asyncio.run(main())

JavaScript Example

JavaScript
const SCAVIO_API_KEY = "your-api-key";
const TARGET_DOMAIN = "scavio.dev";
const KEYWORDS = [
  "best API for web scraping 2026",
  "cheap search API for agents",
  "MCP server search tool"
];

async function checkRanking(keyword) {
  const resp = await fetch("https://api.scavio.dev/api/v1/search", {
    method: "POST",
    headers: { "x-api-key": SCAVIO_API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ query: keyword, num_results: 10 })
  });
  const data = await resp.json();
  const results = data.results || [];
  for (let i = 0; i < results.length; i++) {
    if ((results[i].url || "").includes(TARGET_DOMAIN)) {
      return { position: i + 1, url: results[i].url };
    }
  }
  return { position: null, url: null };
}

async function main() {
  const rankings = [];
  for (const kw of KEYWORDS) {
    const rank = await checkRanking(kw);
    rankings.push({ keyword: kw, ...rank });
  }
  const cost = KEYWORDS.length * 0.005;
  console.log("Rankings:", JSON.stringify(rankings, null, 2));
  console.log("Daily cost: $" + cost.toFixed(3));
  console.log("Weekly cost: $" + (cost * 7).toFixed(3));
}

main();

Expected Output

JSON
Tracked 3 keywords | Cost: $0.015 | Weekly: $0.105

Related Tutorials

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.11+. A Scavio API key from https://scavio.dev. SQLite3 (included with Python). A cron scheduler or Task Scheduler for daily runs. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, 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.

Start Building

Track 50 keywords daily for $0.035/week using the Scavio Search API. Store rank history in SQLite and get alerts when positions shift by 3+ spots.