DEV Community

Elowen
Elowen

Posted on

How to Build a Google Rank Tracker with TalorData SERP API

Tracking where your domain ranks for a list of keywords on Google sounds like a simple one-afternoon project. And it is—until you try to scrape google.com yourself.

After a few hundred requests from the same IP, you start getting the consent page, then a CAPTCHA, then nothing at all. Rotating proxies and headless browsers work for a while, but you end up spending more time keeping the scraper alive than actually using the data.

Easier path: hand that problem to a SERP API and keep your code focused on the rank itself.

In this tutorial, we'll build a complete Google rank tracker in Python using TalorData SERP API—from a single request to a full tracking system with SQLite persistence.


What We're Building

A Python script that:

  1. Takes a list of keywords and a target domain
  2. Queries Google via TalorData SERP API
  3. Finds the target domain's position in organic results
  4. Stores historical rankings in SQLite
  5. Retrieves ranking history for trend visualization

Why TalorData?

TalorData provides a multi-engine SERP API that returns structured search results from Google, Bing, Yandex, and DuckDuckGo through a single endpoint.

Feature Details
Free tier 1,000 free API responses, no credit card required
Pricing ~$0.25–$1.00 per 1,000 successful requests
Engines Google, Bing, Yandex, DuckDuckGo
Output Structured JSON (or raw HTML)
Geo-targeting Location-specific results

Cost comparison: SerpAPI charges ~$10 per 1,000 requests, while TalorData is ~$0.25–$1.00. For 50 keywords tracked daily, that's about $1.50/month vs $15/month.


Step 1: Get Your API Token

  1. Sign up at talordata.com
  2. Navigate to the dashboard and grab your Bearer Token (API token)
  3. Export it as an environment variable:
export TALORDATA_TOKEN="your_api_token_here"
Enter fullscreen mode Exit fullscreen mode

Or hardcode it (not recommended for production):

TOKEN = "your_api_token_here"
Enter fullscreen mode Exit fullscreen mode

Step 2: Send Your First Request

TalorData's SERP API accepts POST requests with form-encoded parameters. Here's the minimal curl example:

curl -X POST "https://serpapi.talordata.net/serp/v1/request" \
  -H "Authorization: Bearer $TALORDATA_TOKEN" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "engine=google" \
  -d "q=serp api" \
  -d "device=desktop" \
  -d "location=United States" \
  -d "num=10" \
  -d "json=1"
Enter fullscreen mode Exit fullscreen mode

Key parameters:

Parameter Description
engine Search engine: google, bing, yandex, duckduckgo
q Search query/keyword
device desktop or mobile
location Geo-target (e.g., "United States")
num Number of results to return (max 100)
json 1 or 2 for structured JSON output

The response includes top-level keys like:

  • organic — organic search results (where the rank lives)
  • sponsored_results — paid ads
  • people_also_ask — related questions
  • pagination — next page info
  • search_information — metadata about the search

Step 3: Parse Rankings in Python

Here's a complete Python function that sends a query and extracts the target domain's position:

import os
import requests

ENDPOINT = "https://serpapi.talordata.net/serp/v1/request"
TOKEN = os.environ.get("TALORDATA_TOKEN")

def search(query: str, location: str = "United States", num: int = 10) -> dict:
    """Send a search request to TalorData SERP API and return the JSON response."""
    resp = requests.post(
        ENDPOINT,
        headers={
            "Authorization": f"Bearer {TOKEN}",
            "Content-Type": "application/x-www-form-urlencoded",
        },
        data={
            "engine": "google",
            "q": query,
            "device": "desktop",
            "location": location,
            "num": num,
            "json": 1,
        },
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json()


def find_rank(data: dict, target_domain: str) -> int | None:
    """
    Find the rank (position) of a target domain in the organic search results.
    Returns None if the domain is not found.
    """
    for item in data.get("organic", []):
        link = item.get("link", "")
        # Extract domain from link for matching
        if target_domain.lower() in link.lower():
            return item.get("position")
    return None


# Example usage
if __name__ == "__main__":
    results = search("serp api")

    # Print top 5 results
    for r in results.get("organic", [])[:5]:
        print(f'{r["position"]:>2} {r.get("display_link", "")}')
        print(f'    {r.get("title", "")}')

    # Find where talordata.com ranks
    rank = find_rank(results, "talordata.com")
    print(f"\nTalorData rank for 'serp api': {rank if rank else 'Not found'}")
Enter fullscreen mode Exit fullscreen mode

Expected output (actual results will vary):

 1 https://serpapi.com
    SerpApi: Google Search API
 2 https://brightdata.com/products/serp-api
    SERP API - SERP Scraper API - Free Trial
 3 https://dataforseo.com/APIs
    SERP API You Can Trust
 4 https://serper.dev
    Serper - The World's Fastest and Cheapest Google Search API
 5 https://github.com/serpapi/google-search-results-python
    serpapi/google-search-results-python

TalorData rank for 'serp api': Not found
Enter fullscreen mode Exit fullscreen mode

Each entry in organic contains:

  • position — the rank (1 = first result)
  • title — page title
  • link — full URL
  • display_link — breadcrumb URL shown under the title
  • description — snippet text

Note: For domain matching, use the host of link rather than display_link, as display_link is Google's rendered version and may differ.


Step 4: Build the Full Rank Tracker

Now let's build a complete tracker that:

  • Tracks multiple keywords
  • Stores historical data in SQLite
  • Retrieves history for trend analysis

Complete Code

import os
import sqlite3
from datetime import datetime
from typing import Optional

import requests

ENDPOINT = "https://serpapi.talordata.net/serp/v1/request"


class RankTracker:
    def __init__(self, api_token: str, db_path: str = "rank_tracker.db"):
        self.api_token = api_token
        self.conn = sqlite3.connect(db_path)
        self._init_db()

    def _init_db(self) -> None:
        """Initialize the SQLite database table."""
        cursor = self.conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS rankings (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                keyword TEXT NOT NULL,
                domain TEXT NOT NULL,
                rank INTEGER,
                checked_at TIMESTAMP NOT NULL
            )
        """)
        self.conn.commit()

    def check_rank(self, keyword: str, target_domain: str, location: str = "United States") -> Optional[int]:
        """
        Query Google for a keyword and return the rank of the target domain.
        Returns None if the domain is not found in the top results.
        """
        headers = {
            "Authorization": f"Bearer {self.api_token}",
            "Content-Type": "application/x-www-form-urlencoded",
        }
        data = {
            "engine": "google",
            "q": keyword,
            "device": "desktop",
            "location": location,
            "num": 100,  # Get top 100 results
            "json": 1,
        }

        resp = requests.post(ENDPOINT, headers=headers, data=data, timeout=60)
        resp.raise_for_status()
        results = resp.json()

        for item in results.get("organic", []):
            link = item.get("link", "")
            if target_domain.lower() in link.lower():
                return item.get("position")

        return None

    def save_rank(self, keyword: str, domain: str, rank: Optional[int]) -> None:
        """Save a ranking record to the database."""
        cursor = self.conn.cursor()
        cursor.execute(
            "INSERT INTO rankings (keyword, domain, rank, checked_at) VALUES (?, ?, ?, ?)",
            (keyword, domain, rank, datetime.utcnow()),
        )
        self.conn.commit()

    def track_keywords(self, keywords: list[str], domain: str, location: str = "United States") -> list[dict]:
        """
        Track multiple keywords for a single domain.
        Returns a list of results with keyword, rank, and timestamp.
        """
        results = []
        for keyword in keywords:
            rank = self.check_rank(keyword, domain, location)
            self.save_rank(keyword, domain, rank)
            results.append({
                "keyword": keyword,
                "rank": rank,
                "checked_at": datetime.utcnow().isoformat(),
            })
            print(f"{keyword}: {rank if rank else 'Not found'}")
        return results

    def get_history(self, keyword: str, domain: str, limit: int = 30) -> list[tuple]:
        """
        Get historical rankings for a specific keyword and domain.
        Returns a list of (rank, checked_at) tuples, most recent first.
        """
        cursor = self.conn.cursor()
        cursor.execute(
            "SELECT rank, checked_at FROM rankings "
            "WHERE keyword = ? AND domain = ? "
            "ORDER BY checked_at DESC LIMIT ?",
            (keyword, domain, limit),
        )
        return cursor.fetchall()

    def get_latest_rank(self, keyword: str, domain: str) -> Optional[int]:
        """Get the most recent rank for a keyword and domain."""
        cursor = self.conn.cursor()
        cursor.execute(
            "SELECT rank FROM rankings "
            "WHERE keyword = ? AND domain = ? "
            "ORDER BY checked_at DESC LIMIT 1",
            (keyword, domain),
        )
        row = cursor.fetchone()
        return row[0] if row else None

    def close(self) -> None:
        """Close the database connection."""
        self.conn.close()


# Example usage
if __name__ == "__main__":
    API_TOKEN = os.environ.get("TALORDATA_TOKEN")
    if not API_TOKEN:
        raise ValueError("Please set TALORDATA_TOKEN environment variable")

    tracker = RankTracker(API_TOKEN)

    # Keywords to track
    keywords = [
        "serp api",
        "google search api",
        "best serp api 2026",
        "seo rank tracker",
        "search engine results api",
    ]

    # Your domain
    domain = "talordata.com"

    # Run the tracker
    print("Tracking keywords...")
    tracker.track_keywords(keywords, domain)

    # Check history for one keyword
    print("\nHistory for 'serp api':")
    history = tracker.get_history("serp api", domain, limit=10)
    for rank, checked_at in history:
        print(f"  {checked_at}: rank {rank if rank else 'Not found'}")

    tracker.close()
Enter fullscreen mode Exit fullscreen mode

What This Code Does

  1. check_rank() — Sends a request to TalorData, iterates through organic results, and returns the position where the target domain appears.

  2. save_rank() — Stores each check with a timestamp in SQLite.

  3. track_keywords() — Loops through a list of keywords, checks each one, saves the results, and prints progress.

  4. get_history() — Retrieves historical rankings for a specific keyword/domain pair, ordered by date (most recent first).

  5. get_latest_rank() — Quick lookup for the most recent rank without fetching all history.


Step 5: Visualize the Data (Optional)

With historical data in SQLite, you can easily visualize ranking trends. Here's a quick example using matplotlib:

import matplotlib.pyplot as plt
from datetime import datetime

def plot_rank_history(tracker, keyword: str, domain: str):
    history = tracker.get_history(keyword, domain, limit=30)
    if not history:
        print("No history found.")
        return

    # Reverse to plot chronological (oldest to newest)
    history.reverse()
    dates = [datetime.fromisoformat(str(ts).replace(" ", "T")) for _, ts in history]
    ranks = [r if r is not None else 101 for r, _ in history]  # 101 = not found

    plt.figure(figsize=(10, 5))
    plt.plot(dates, ranks, marker="o")
    plt.gca().invert_yaxis()  # Lower rank is better
    plt.xlabel("Date")
    plt.ylabel("Rank")
    plt.title(f"Ranking History: '{keyword}'")
    plt.grid(True, alpha=0.3)
    plt.show()

# Usage
# plot_rank_history(tracker, "serp api", "talordata.com")
Enter fullscreen mode Exit fullscreen mode

Step 6: Automate with a Scheduler

To run this automatically (e.g., daily), you can use:

Option 1: Cron job (Linux/macOS)

# Run daily at 9 AM
0 9 * * * cd /path/to/project && python rank_tracker.py
Enter fullscreen mode Exit fullscreen mode

Option 2: GitHub Actions
Create .github/workflows/rank-track.yml:

name: Track Rankings
on:
  schedule:
    - cron: '0 9 * * *'  # Daily at 9 AM UTC
jobs:
  track:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: pip install requests
      - run: python rank_tracker.py
        env:
          TALORDATA_TOKEN: ${{ secrets.TALORDATA_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Cost Analysis

Let's calculate the monthly cost for tracking 50 keywords daily:

Metric Value
Keywords 50
Checks per day 50
Checks per month 50 × 30 = 1,500
TalorData price ~$0.25–$1.00 per 1,000 requests
Monthly cost ~$1.35–$1.50

Compare this to building and maintaining your own scraper:

Approach Monthly Cost Maintenance Time
Self-built scraper $20–50+ (proxies) Several hours/week
TalorData SERP API ~$1.50 0

Supported Search Engines

TalorData supports multiple search engines through the same endpoint:

Engine engine parameter
Google google
Bing bing
Yandex yandex
DuckDuckGo duckduckgo

To switch engines, simply change the engine parameter in your request:

data = {
    "engine": "bing",  # or "yandex", "duckduckgo"
    "q": keyword,
    # ... other parameters
}
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls and Tips

  1. Domain matching: Use the host of link rather than display_link for reliable matching.

  2. Rate limiting: TalorData charges per successful request—failed requests are not billed. Still, cache results for stable keywords to save costs.

  3. Location matters: Rankings vary significantly by geographic location. Always specify location for consistent results.

  4. Free tier: You get 1,000 free requests after signing up—more than enough to test and build your MVP.


Next Steps

Once your rank tracker is running, consider these enhancements:

  • Multi-domain tracking — Monitor competitors alongside your own domain
  • Alert system — Send email/Slack notifications when rankings change significantly
  • Dashboard — Build a web UI with Streamlit or Flask to visualize trends
  • Multi-engine tracking — Check rankings on Bing and DuckDuckGo too
  • SERP feature extraction — Track "People Also Ask" questions and featured snippets for content ideas

Summary

In ~100 lines of Python, we built a complete Google rank tracker that:

  • Fetches search results via TalorData SERP API (no scraping, no CAPTCHAs)
  • Extracts target domain positions from organic results
  • Persists historical data to SQLite
  • Retrieves ranking history for analysis

Why this works:

  • TalorData handles the hard parts (proxies, parsing, rate limits)
  • You focus on the data and what it means
  • Cost is minimal (~$1.50/month for 50 keywords daily)
  • 1,000 free requests to get started

Get started today: Sign up at talordata.com, grab your free 1,000 requests, and start tracking your rankings in minutes.


Have questions? Drop them in the comments below. Happy tracking!

Top comments (0)