Tutorial

How to Build a DIY Keyword Rank Tracker

Build a keyword rank tracker from raw SERP data. Track positions daily in SQLite, detect changes, and alert on rank movements. Python at $0.005/keyword.

Commercial rank trackers charge $30-300/month and check keywords once daily. Building your own tracker with a SERP API gives you full control over check frequency, stored data, and alerting logic. Each keyword check costs $0.005 through Scavio, so tracking 100 keywords daily runs $0.50/day or $15/month, less than most starter plans.

Prerequisites

  • Python 3.8+
  • requests library
  • A Scavio API key from scavio.dev
  • SQLite3 (included with Python)

Walkthrough

Step 1: Set up the rank tracking database

Create a SQLite database to store daily rank positions.

Python
import os, requests, json, sqlite3
from datetime import datetime

API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}

db = sqlite3.connect('rank_tracker.db')
db.execute('''CREATE TABLE IF NOT EXISTS ranks (
    keyword TEXT, domain TEXT, position INTEGER,
    title TEXT, checked_at TEXT
)''')
db.execute('CREATE INDEX IF NOT EXISTS idx_kw ON ranks(keyword, checked_at)')
db.commit()

MY_DOMAIN = 'scavio.dev'
KEYWORDS = ['serp api', 'search api python', 'google search api',
            'web scraping api', 'ai agent search tool']
print(f'Tracking {len(KEYWORDS)} keywords for {MY_DOMAIN}')

Step 2: Check keyword rankings

Fetch SERP results and find your domain position for each keyword.

Python
def check_rank(keyword, domain):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': keyword, 'country_code': 'us'}).json()
    organic = data.get('organic_results', [])
    now = datetime.now().isoformat()
    for r in organic[:50]:
        link = r.get('link', '')
        if domain in link:
            pos = r.get('position', 0)
            title = r.get('title', '')[:100]
            db.execute('INSERT INTO ranks VALUES (?,?,?,?,?)',
                (keyword, domain, pos, title, now))
            db.commit()
            return pos
    db.execute('INSERT INTO ranks VALUES (?,?,?,?,?)',
        (keyword, domain, 0, 'Not found', now))
    db.commit()
    return 0

for kw in KEYWORDS:
    pos = check_rank(kw, MY_DOMAIN)
    status = f'#{pos}' if pos else 'Not in top 50'
    print(f'  {kw:30} | {status}')
print(f'Cost: ${len(KEYWORDS) * 0.005:.3f}')

Step 3: Detect rank changes between checks

Compare current positions against previous checks to find movements.

Python
def detect_changes(keyword, domain):
    rows = db.execute(
        'SELECT position, checked_at FROM ranks WHERE keyword=? AND domain=? ORDER BY checked_at DESC LIMIT 2',
        (keyword, domain)).fetchall()
    if len(rows) < 2:
        return None
    current, prev = rows[0][0], rows[1][0]
    if current == prev:
        return None
    change = prev - current  # positive = improved
    return {'keyword': keyword, 'prev': prev, 'current': current, 'change': change}

def report_changes():
    changes = []
    for kw in KEYWORDS:
        c = detect_changes(kw, MY_DOMAIN)
        if c:
            changes.append(c)
    if changes:
        print(f'\n{len(changes)} rank changes:')
        for c in changes:
            arrow = 'UP' if c['change'] > 0 else 'DOWN'
            print(f'  {c["keyword"]:30} | #{c["prev"]} -> #{c["current"]} ({arrow} {abs(c["change"])})')
    else:
        print('No rank changes detected.')
    return changes

report_changes()

Step 4: Schedule daily checks via cron

Automate the tracker to run daily and accumulate history.

Python
def daily_check():
    print(f'Daily rank check: {datetime.now().isoformat()}')
    results = []
    for kw in KEYWORDS:
        pos = check_rank(kw, MY_DOMAIN)
        results.append({'keyword': kw, 'position': pos})
    cost = len(KEYWORDS) * 0.005
    print(f'Checked {len(KEYWORDS)} keywords. Cost: ${cost:.3f}')
    changes = report_changes()
    # Summary
    ranked = [r for r in results if r['position'] > 0]
    avg = sum(r['position'] for r in ranked) / len(ranked) if ranked else 0
    print(f'\nSummary: {len(ranked)}/{len(results)} keywords ranking, avg position: {avg:.1f}')
    print(f'Monthly cost estimate: ${cost * 30:.2f}')

# Cron: 0 6 * * * cd /path/to/project && python rank_tracker.py
daily_check()

Python Example

Python
import os, requests
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}

def track(keyword, domain):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': keyword, 'country_code': 'us'}).json()
    for r in data.get('organic_results', [])[:50]:
        if domain in r.get('link', ''):
            print(f'{keyword}: {domain} at #{r["position"]}. Cost: $0.005')
            return r['position']
    print(f'{keyword}: {domain} not in top 50')
    return 0

track('serp api', 'scavio.dev')

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
async function track(keyword, domain) {
  const data = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST', headers: SH,
    body: JSON.stringify({ query: keyword, country_code: 'us' })
  }).then(r => r.json());
  const match = (data.organic_results || []).find(r => (r.link || '').includes(domain));
  console.log(match ? `${keyword}: ${domain} at #${match.position}` : `${keyword}: not found`);
}
track('serp api', 'scavio.dev').catch(console.error);

Expected Output

JSON
Tracking 5 keywords for scavio.dev
  serp api                       | #4
  search api python              | #7
  google search api              | #11
  web scraping api               | #6
  ai agent search tool           | #3
Cost: $0.025

Summary: 5/5 keywords ranking, avg position: 6.2
Monthly cost estimate: $0.75

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.8+. requests library. A Scavio API key from scavio.dev. SQLite3 (included with Python). 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

Build a keyword rank tracker from raw SERP data. Track positions daily in SQLite, detect changes, and alert on rank movements. Python at $0.005/keyword.