ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. How to Build a Competitive SERP Monitor
Tutorial

How to Build a Competitive SERP Monitor

Monitor competitor rankings across keywords daily, detect position changes, and alert on new entrants. Python pipeline at $0.005/check.

Get Free API KeyAPI Docs

Rank tracking tools charge $50-300/month and update once per day at best. A custom competitive SERP monitor checks exactly the keywords you care about, at your own frequency, and alerts on the changes that matter to your business. This tutorial builds a monitor that tracks multiple competitors across keyword sets at $0.005/check, with Slack or email alerting on position swings.

Prerequisites

  • Python 3.8+
  • requests library
  • A Scavio API key from scavio.dev
  • Competitor domains and target keywords

Walkthrough

Step 1: Define competitors and keywords

Set up the monitoring configuration.

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'}

CONFIG = {
    'competitors': ['serpapi.com', 'dataforseo.com', 'serper.dev', 'exa.ai', 'tavily.com'],
    'keywords': [
        'serp api', 'search api for developers', 'google search api python',
        'web scraping api 2026', 'ai agent search api'
    ]
}

db = sqlite3.connect('serp_monitor.db')
db.execute('''CREATE TABLE IF NOT EXISTS rankings (
    keyword TEXT, domain TEXT, position INTEGER,
    checked_at TEXT, title TEXT
)''')
db.commit()
print(f'Monitoring {len(CONFIG["competitors"])} competitors across {len(CONFIG["keywords"])} keywords')
print(f'Daily cost: ${len(CONFIG["keywords"]) * 0.005:.3f}')

Step 2: Check rankings for all keywords

Run a SERP check for each keyword and record competitor positions.

Python
def check_rankings(keywords, competitors):
    results = []
    for kw in keywords:
        data = requests.post('https://api.scavio.dev/api/v1/search',
            headers=SH, json={'query': kw, 'country_code': 'us'}).json()
        organic = data.get('organic_results', [])
        now = datetime.now().isoformat()
        for r in organic[:20]:
            domain = r.get('link', '').split('/')[2] if r.get('link') else ''
            domain = domain.replace('www.', '')
            if domain in competitors:
                pos = r.get('position', 99)
                db.execute('INSERT INTO rankings VALUES (?,?,?,?,?)',
                    (kw, domain, pos, now, r.get('title', '')[:80]))
                results.append({'keyword': kw, 'domain': domain, 'position': pos})
        db.commit()
    print(f'Checked {len(keywords)} keywords. Cost: ${len(keywords) * 0.005:.3f}')
    return results

ranks = check_rankings(CONFIG['keywords'], CONFIG['competitors'])
for r in ranks[:10]:
    print(f'  {r["keyword"]:30} | {r["domain"]:20} | #{r["position"]}')

Step 3: Detect position changes

Compare current check against previous to find movers.

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

def alert_changes(keywords, competitors, threshold=3):
    alerts = []
    for kw in keywords:
        for comp in competitors:
            change = detect_changes(kw, comp, threshold)
            if change: alerts.append(change)
    if alerts:
        print(f'\n{len(alerts)} position changes detected:')
        for a in alerts:
            direction = 'UP' if a['change'] > 0 else 'DOWN'
            print(f'  {a["domain"]:20} | {a["keyword"]:25} | #{a["previous"]} -> #{a["current"]} ({direction} {abs(a["change"])})')
    else:
        print('No significant position changes.')
    return alerts

alert_changes(CONFIG['keywords'], CONFIG['competitors'])

Step 4: Generate competitive dashboard data

Build a summary view of the competitive landscape.

Python
def dashboard(competitors, keywords):
    print(f'\n{"Keyword":30} | ', end='')
    for c in competitors[:5]: print(f'{c[:12]:12} | ', end='')
    print()
    print('-' * (32 + 15 * len(competitors[:5])))
    for kw in keywords:
        print(f'{kw[:30]:30} | ', end='')
        for comp in competitors[:5]:
            row = db.execute(
                'SELECT position FROM rankings WHERE keyword=? AND domain=? ORDER BY checked_at DESC LIMIT 1',
                (kw, comp)).fetchone()
            pos = f'#{row[0]}' if row else '-'
            print(f'{pos:12} | ', end='')
        print()
    total_checks = len(keywords)
    print(f'\nMonthly cost estimate: ${total_checks * 0.005 * 30:.2f} (daily) or ${total_checks * 0.005 * 7:.2f} (weekly)')

# Run a check first, then display
check_rankings(CONFIG['keywords'], CONFIG['competitors'])
dashboard(CONFIG['competitors'], CONFIG['keywords'])

Python Example

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

def check(keyword, competitors):
    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', [])[:20]:
        domain = r.get('link', '').split('/')[2].replace('www.', '')
        if domain in competitors:
            print(f'{keyword}: {domain} at #{r["position"]}')

check('serp api', ['serpapi.com', 'dataforseo.com', 'serper.dev'])

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
async function check(keyword, competitors) {
  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());
  for (const r of (data.organic_results || []).slice(0, 20)) {
    const domain = new URL(r.link).hostname.replace('www.', '');
    if (competitors.includes(domain))
      console.log(`${keyword}: ${domain} at #${r.position}`);
  }
}
check('serp api', ['serpapi.com', 'dataforseo.com', 'serper.dev']).catch(console.error);

Expected Output

JSON
Monitoring 5 competitors across 5 keywords
Daily cost: $0.025
Checked 5 keywords. Cost: $0.025

Keyword                        | serpapi.com  | dataforseo.c | serper.dev   | exa.ai       | tavily.com   |
-----------------------------------------------------------------------------------------------
serp api                       | #3           | #7           | #5           | #12          | #15          |
search api for developers      | #4           | #8           | #6           | #9           | -            |
google search api python       | #2           | #5           | #4           | -            | -            |
web scraping api 2026          | #6           | #3           | #8           | -            | -            |
ai agent search api            | #5           | -            | #7           | #4           | #11          |

Monthly cost estimate: $3.75 (daily) or $0.88 (weekly)

Related Tutorials

  • How to Build a Historical SERP Archive
  • How to Build a Custom SEO Dashboard with a Search API
  • How to Monitor Search Surfaces Beyond Rank

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. Competitor domains and target keywords. 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 Competitor Monitoring API in 2026

Read more
Best Of

Best SEO Rank Tracking APIs in 2026

Read more
Solution

Automated Competitor SERP Tracking

Read more
Solution

Build Reliable Local Rank Tracking with Scavio API

Read more
Use Case

SEO Light User API Workflow

Read more
Workflow

Weekly Rank Tracking API Pipeline

Read more

Start Building

Monitor competitor rankings across keywords daily, detect position changes, and alert on new entrants. Python pipeline at $0.005/check.

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