DEV Community

Cecilia Hill
Cecilia Hill

Posted on

How to Use a SERP API with Python to Track Google Rankings

Tracking Google rankings sounds simple at first.

You have a keyword.

You search it on Google.

You check where your website appears.

That works if you only have one keyword.

But once you need to track 50, 500, or 5,000 keywords across different countries, cities, languages, or devices, manual checking stops making sense.

You need a repeatable workflow.

A simple ranking tracker usually looks like this:

Keyword list → SERP API → organic results → ranking check → CSV/report
Enter fullscreen mode Exit fullscreen mode

In this tutorial, we’ll build a basic Google ranking tracker with Python and a SERP API.

We will:

  1. Load a list of keywords
  2. Send each keyword to a SERP API
  3. Extract Google organic results
  4. Check whether a target domain appears
  5. Save ranking data to a CSV file

This is not a full SEO platform, but it gives you the core logic behind many rank tracking tools.

Why use a SERP API?

You can try to scrape Google directly.

For a small experiment, it may work. But production scraping gets messy quickly.

Google result pages can change by:

  • country
  • city
  • language
  • device
  • search intent
  • query type
  • result features

A single result page may include:

  • organic results
  • ads
  • local packs
  • maps
  • images
  • videos
  • shopping results
  • news results
  • People Also Ask
  • related searches

Then there are the operational issues:

  • blocked requests
  • CAPTCHA
  • proxy management
  • unstable HTML
  • parser maintenance
  • retry logic
  • localization problems

If your real goal is to track rankings, you probably do not want to maintain all of that infrastructure.

A SERP API helps by returning structured search results in JSON.

Instead of parsing raw HTML, you get data like:

{
  "query": "best project management software",
  "organic_results": [
    {
      "position": 1,
      "title": "Best Project Management Software Tools",
      "link": "https://example.com",
      "snippet": "Compare project management tools and pricing..."
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

That is much easier to work with in Python.

What we are building

Let’s say we want to track whether our target domain appears for several keywords.

Example target domain:

example.com
Enter fullscreen mode Exit fullscreen mode

Example keywords:

best project management software
project management tools
task management app
team collaboration software
Enter fullscreen mode Exit fullscreen mode

For each keyword, we want to collect:

  • keyword
  • target domain
  • ranking position
  • result title
  • result URL
  • snippet
  • whether the domain was found

Example CSV output:

keyword,target_domain,found,position,title,url
best project management software,example.com,yes,3,Example Project Tool,https://example.com
task management app,example.com,no,,, 
Enter fullscreen mode Exit fullscreen mode

Install dependencies

We only need two Python packages:

pip install requests python-dotenv
Enter fullscreen mode Exit fullscreen mode

requests is used to call the SERP API.

python-dotenv is used to load your API key from a .env file.

Create a .env file

Create a file named .env:

SERP_API_KEY=your_api_key_here
SERP_API_URL=https://your-serp-api-endpoint.example.com/search
Enter fullscreen mode Exit fullscreen mode

Do not hardcode API keys directly in your script.

The exact endpoint depends on your SERP API provider. You can use providers such as SerpApi, Serper, SearchAPI, Bright Data, or Talordata. In this tutorial, we’ll use a generic request format so you can adapt it to your provider.

Create a keyword list

Create a file named keywords.txt:

best project management software
project management tools
task management app
team collaboration software
Enter fullscreen mode Exit fullscreen mode

Each line is one keyword.

Basic SERP API request

Create a file named rank_tracker.py.

import os
import requests
from dotenv import load_dotenv


load_dotenv()

SERP_API_KEY = os.getenv("SERP_API_KEY")
SERP_API_URL = os.getenv("SERP_API_URL")


def fetch_google_results(query, location="United States", language="en"):
    if not SERP_API_KEY:
        raise ValueError("Missing SERP_API_KEY environment variable")

    if not SERP_API_URL:
        raise ValueError("Missing SERP_API_URL environment variable")

    params = {
        "api_key": SERP_API_KEY,
        "engine": "google",
        "q": query,
        "location": location,
        "language": language,
        "output": "json",
    }

    response = requests.get(SERP_API_URL, params=params, timeout=30)
    response.raise_for_status()

    return response.json()
Enter fullscreen mode Exit fullscreen mode

Different providers may use different parameter names.

For example:

  • q or query
  • location, gl, or country
  • language, hl, or locale
  • engine, search_type, or type

Check your provider’s documentation and adjust the parameters.

Extract organic results

Most SERP APIs return organic results in a list.

Common response keys include:

  • organic_results
  • organic
  • results

Let’s write a helper function that supports several possible keys.

def get_organic_results(data):
    possible_keys = [
        "organic_results",
        "organic",
        "results",
    ]

    for key in possible_keys:
        value = data.get(key)
        if isinstance(value, list):
            return value

    return []
Enter fullscreen mode Exit fullscreen mode

Now normalize each result.

def normalize_result(item):
    return {
        "position": item.get("position") or item.get("rank"),
        "title": item.get("title") or "",
        "url": item.get("link") or item.get("url") or "",
        "snippet": item.get("snippet") or item.get("description") or "",
    }
Enter fullscreen mode Exit fullscreen mode

This gives us a clean format no matter which provider we use.

Check whether a domain appears

Now we need to check whether the target domain appears in the organic results.

We can use Python’s urllib.parse to extract the domain from each URL.

from urllib.parse import urlparse


def extract_domain(url):
    if not url:
        return ""

    parsed = urlparse(url)
    domain = parsed.netloc.lower()

    if domain.startswith("www."):
        domain = domain[4:]

    return domain
Enter fullscreen mode Exit fullscreen mode

Now write the ranking check function:

def find_domain_ranking(results, target_domain):
    target_domain = target_domain.lower().replace("www.", "")

    for result in results:
        result_domain = extract_domain(result.get("url", ""))

        if result_domain == target_domain or result_domain.endswith("." + target_domain):
            return {
                "found": "yes",
                "position": result.get("position"),
                "title": result.get("title"),
                "url": result.get("url"),
                "snippet": result.get("snippet"),
            }

    return {
        "found": "no",
        "position": "",
        "title": "",
        "url": "",
        "snippet": "",
    }
Enter fullscreen mode Exit fullscreen mode

This checks exact domain matches and subdomains.

For example, it can match:

example.com
blog.example.com
www.example.com
Enter fullscreen mode Exit fullscreen mode

Load keywords from a file

Now let’s read keywords from keywords.txt.

def load_keywords(filename="keywords.txt"):
    with open(filename, "r", encoding="utf-8") as file:
        return [
            line.strip()
            for line in file
            if line.strip()
        ]
Enter fullscreen mode Exit fullscreen mode

Save ranking results to CSV

We can save the final ranking data to a CSV file.

import csv


def save_rankings_to_csv(rows, filename="rankings.csv"):
    fieldnames = [
        "keyword",
        "target_domain",
        "found",
        "position",
        "title",
        "url",
        "snippet",
    ]

    with open(filename, mode="w", newline="", encoding="utf-8") as file:
        writer = csv.DictWriter(file, fieldnames=fieldnames)
        writer.writeheader()

        for row in rows:
            writer.writerow(row)
Enter fullscreen mode Exit fullscreen mode

Full script

Here is the complete version:

import os
import csv
import requests
from urllib.parse import urlparse
from dotenv import load_dotenv


load_dotenv()

SERP_API_KEY = os.getenv("SERP_API_KEY")
SERP_API_URL = os.getenv("SERP_API_URL")


def fetch_google_results(query, location="United States", language="en"):
    if not SERP_API_KEY:
        raise ValueError("Missing SERP_API_KEY environment variable")

    if not SERP_API_URL:
        raise ValueError("Missing SERP_API_URL environment variable")

    params = {
        "api_key": SERP_API_KEY,
        "engine": "google",
        "q": query,
        "location": location,
        "language": language,
        "output": "json",
    }

    response = requests.get(SERP_API_URL, params=params, timeout=30)
    response.raise_for_status()

    return response.json()


def get_organic_results(data):
    possible_keys = [
        "organic_results",
        "organic",
        "results",
    ]

    for key in possible_keys:
        value = data.get(key)
        if isinstance(value, list):
            return value

    return []


def normalize_result(item):
    return {
        "position": item.get("position") or item.get("rank"),
        "title": item.get("title") or "",
        "url": item.get("link") or item.get("url") or "",
        "snippet": item.get("snippet") or item.get("description") or "",
    }


def extract_domain(url):
    if not url:
        return ""

    parsed = urlparse(url)
    domain = parsed.netloc.lower()

    if domain.startswith("www."):
        domain = domain[4:]

    return domain


def find_domain_ranking(results, target_domain):
    target_domain = target_domain.lower().replace("www.", "")

    for result in results:
        result_domain = extract_domain(result.get("url", ""))

        if result_domain == target_domain or result_domain.endswith("." + target_domain):
            return {
                "found": "yes",
                "position": result.get("position"),
                "title": result.get("title"),
                "url": result.get("url"),
                "snippet": result.get("snippet"),
            }

    return {
        "found": "no",
        "position": "",
        "title": "",
        "url": "",
        "snippet": "",
    }


def load_keywords(filename="keywords.txt"):
    with open(filename, "r", encoding="utf-8") as file:
        return [
            line.strip()
            for line in file
            if line.strip()
        ]


def save_rankings_to_csv(rows, filename="rankings.csv"):
    fieldnames = [
        "keyword",
        "target_domain",
        "found",
        "position",
        "title",
        "url",
        "snippet",
    ]

    with open(filename, mode="w", newline="", encoding="utf-8") as file:
        writer = csv.DictWriter(file, fieldnames=fieldnames)
        writer.writeheader()

        for row in rows:
            writer.writerow(row)


def track_rankings(target_domain, keywords, location="United States", language="en"):
    rows = []

    for keyword in keywords:
        print(f"Checking keyword: {keyword}")

        try:
            data = fetch_google_results(
                query=keyword,
                location=location,
                language=language,
            )

            organic_items = get_organic_results(data)
            normalized_results = [
                normalize_result(item)
                for item in organic_items
            ]

            ranking = find_domain_ranking(
                results=normalized_results,
                target_domain=target_domain,
            )

            rows.append({
                "keyword": keyword,
                "target_domain": target_domain,
                "found": ranking["found"],
                "position": ranking["position"],
                "title": ranking["title"],
                "url": ranking["url"],
                "snippet": ranking["snippet"],
            })

        except requests.RequestException as error:
            print(f"Request failed for keyword: {keyword}")
            print(error)

            rows.append({
                "keyword": keyword,
                "target_domain": target_domain,
                "found": "error",
                "position": "",
                "title": "",
                "url": "",
                "snippet": "",
            })

    return rows


if __name__ == "__main__":
    target_domain = "example.com"
    keywords = load_keywords("keywords.txt")

    rankings = track_rankings(
        target_domain=target_domain,
        keywords=keywords,
        location="United States",
        language="en",
    )

    save_rankings_to_csv(rankings, "rankings.csv")

    print("Saved ranking results to rankings.csv")
Enter fullscreen mode Exit fullscreen mode

Run the script:

python rank_tracker.py
Enter fullscreen mode Exit fullscreen mode

You should get a file named:

rankings.csv
Enter fullscreen mode Exit fullscreen mode

Example CSV output

Your CSV may look like this:

keyword,target_domain,found,position,title,url,snippet
best project management software,example.com,yes,4,Example Project Tool,https://example.com,Manage projects and tasks...
project management tools,example.com,no,,,,
task management app,example.com,yes,7,Example Task App,https://example.com/tasks,Task management for teams...
Enter fullscreen mode Exit fullscreen mode

This is the basic ranking tracker.

Track rankings by location

Google results are not the same everywhere.

A keyword may rank differently in:

United States
United Kingdom
Canada
Australia
Singapore
Enter fullscreen mode Exit fullscreen mode

A local keyword may change even more by city.

For example:

best digital marketing agency
Enter fullscreen mode Exit fullscreen mode

could return very different results in New York, London, and Sydney.

You can track multiple locations like this:

locations = [
    "United States",
    "United Kingdom",
    "Singapore",
]

all_rows = []

for location in locations:
    rankings = track_rankings(
        target_domain="example.com",
        keywords=keywords,
        location=location,
        language="en",
    )

    for row in rankings:
        row["location"] = location
        all_rows.append(row)
Enter fullscreen mode Exit fullscreen mode

If you add location, update the CSV fieldnames:

fieldnames = [
    "keyword",
    "location",
    "target_domain",
    "found",
    "position",
    "title",
    "url",
    "snippet",
]
Enter fullscreen mode Exit fullscreen mode

Location is important for:

  • local SEO
  • international SEO
  • agency reporting
  • market research
  • competitor monitoring
  • multi-country search analysis

Track multiple domains

You may also want to track competitors.

Example domains:

example.com
competitor-one.com
competitor-two.com
Enter fullscreen mode Exit fullscreen mode

You can loop through multiple domains:

target_domains = [
    "example.com",
    "competitor-one.com",
    "competitor-two.com",
]

all_rows = []

for domain in target_domains:
    rankings = track_rankings(
        target_domain=domain,
        keywords=keywords,
        location="United States",
        language="en",
    )

    all_rows.extend(rankings)

save_rankings_to_csv(all_rows, "competitor_rankings.csv")
Enter fullscreen mode Exit fullscreen mode

This gives you a basic competitor visibility report.

How to improve this script

This is a simple version, but you can extend it in many ways.

You could add:

  • scheduled daily runs with cron
  • SQLite or Postgres storage
  • ranking history
  • position change detection
  • email alerts
  • Slack notifications
  • competitor domain tracking
  • local city-level tracking
  • dashboard visualization
  • AI-generated SEO summaries

For example, once you store rankings over time, you can detect changes like:

example.com moved from position 8 to position 3 for "project management tools"
Enter fullscreen mode Exit fullscreen mode

That can become the basis for a simple SEO monitoring tool.

Using ranking data with an LLM

SERP data is also useful for AI workflows.

Once you have ranking data, you can ask an LLM to summarize it.

Example prompt:

You are an SEO analyst.

Here is Google ranking data for our domain and competitors.

Summarize:
- which keywords we rank for
- which keywords we are missing
- which competitors appear most often
- what actions we should consider next
Enter fullscreen mode Exit fullscreen mode

You can feed the CSV data or a cleaned summary into the prompt.

This is useful for:

  • SEO copilots
  • weekly ranking summaries
  • competitor analysis
  • content planning
  • market research
  • automated reporting

The key is that the LLM should reason over structured ranking data, not raw search pages.

What to check before choosing a SERP API

Before choosing a SERP API provider, test it with your actual keywords.

Do not only test easy queries.

Try:

brand keywords
commercial keywords
local keywords
long-tail keywords
competitor keywords
high-volume keywords
Enter fullscreen mode Exit fullscreen mode

Then check:

  • Does the API return clean JSON?
  • Are organic results complete?
  • Are positions stable?
  • Are titles, links, and snippets available?
  • Does geo-targeting work correctly?
  • Can you request HTML if needed?
  • Are failed requests billed?
  • How much cleanup does your code need?

Providers such as SerpApi, Serper, SearchAPI, Bright Data, DataForSEO, and Talordata can all be tested for this kind of workflow.

The best choice depends on your use case.

If you only need a few simple Google searches, a lightweight API may be enough.

If you need SEO monitoring, competitor tracking, AI search workflows, or multi-engine SERP data, test the response structure more carefully.

Final thoughts

Tracking Google rankings is not only about checking where a page appears.

It is about building a repeatable search data workflow.

With Python and a SERP API, you can:

  • load keywords
  • collect Google results
  • extract organic rankings
  • check target domains
  • save results to CSV
  • compare rankings across locations
  • monitor competitors
  • feed ranking data into AI workflows

You do not need to build and maintain your own Google scraper to get started.

A SERP API gives you structured search data so you can focus on analysis, reporting, and product logic.

If you want to test this workflow, Talordata is one SERP API worth comparing. It supports structured SERP data, JSON / HTML response formats, geo-targeted results, and search workflows for SEO monitoring, AI agents, competitor tracking, and market research.

Talordata also offers 1,000 free API requests after signup, which is enough to test real keywords before choosing a provider.

Top comments (0)