SERP API for Local SEO: Track Rankings by City and Language

Learn how to use a SERP API for local SEO rank tracking. Use Python to collect search results by city, country, language, and device, then export ranking data to CSV.

SERP API for Local SEO: Track Rankings by City and Language
Marcus Bennett
Last updated on
6 min read

Local SEO rank tracking gets tricky fast.

A business may rank well in one city and disappear in another. The same keyword can produce different results by country, language, device, or even neighborhood-level intent. For agencies, franchise brands, SaaS companies with local landing pages, and marketplace teams, one national ranking report is usually not enough.

Local SEO needs location-aware SERP data.

A SERP API helps you collect search results programmatically, so you can track rankings by city, language, country, and device without manually checking Google from different locations.

Quick answer

To track local SEO rankings, send a SERP API request for each keyword and target market, including city, country, language, and device parameters. Then extract organic results, match your target domain, store the ranking position, and compare ranking snapshots over time.

A practical workflow looks like this:

Keyword list
→ City and language list
→ SERP API request
→ Extract organic results
→ Match target domain
→ Store ranking snapshot
→ Compare changes by city and language

Why city-level ranking matters

Local SEO is not only about whether a website ranks. It is about where it ranks.

For example:

Keyword: emergency plumber
City: Austin
Language: English

may produce different results from:

Keyword: emergency plumber
City: Dallas
Language: English

And language adds another layer:

Keyword: dentist near me
City: Miami
Language: English

Keyword: dentista cerca de mí
City: Miami
Language: Spanish

If your report only shows one average ranking, it may hide important local differences. A city-level report can show where visibility is strong, where landing pages are weak, and where competitors are winning.

What should a local SEO tracker collect?

A useful local rank tracking row should include more than keyword and position.

Field

Why it matters

keyword

The query being tracked

target_domain

The website you want to monitor

city

City or local market

country

Country-level market context

language

Search language

device

Desktop or mobile

position

Organic ranking position

matched_url

Exact page that ranked

title

Search result title

snippet

Visible search result description

collected_at

Timestamp for historical comparison

Without location, language, device, and timestamp, a ranking number is easy to misread.

Position 4 in New York mobile search is not the same data point as position 4 in Chicago desktop search.

Organic rankings vs local pack rankings

Local SEO often includes different result types.

Result type

What it tells you

Organic results

Which pages rank in standard web results

Local pack

Which businesses appear in map-style local results

Maps results

How business profiles rank in map search

Ads

Which competitors are paying for visibility

People Also Ask

Which local questions users ask

Shopping results

Which products and sellers appear for local commercial queries

This article focuses on organic ranking tracking, but the same structure can be extended to Maps or local pack workflows. Talordata’s SEO monitoring page describes local SEO monitoring use cases, including map rankings, business profiles, ratings, reviews, and local visibility data.

Step 1: Prepare keywords and locations

Start small. Use a few keywords and a few cities before scaling.

KEYWORDS = [
    "emergency plumber",
    "dentist near me",
    "coffee shop",
]

LOCATIONS = [
    {
        "city": "Austin",
        "country": "us",
        "language": "en",
        "location": "Austin, Texas, United States",
        "device": "desktop",
    },
    {
        "city": "Dallas",
        "country": "us",
        "language": "en",
        "location": "Dallas, Texas, United States",
        "device": "desktop",
    },
    {
        "city": "Miami",
        "country": "us",
        "language": "es",
        "location": "Miami, Florida, United States",
        "device": "mobile",
    },
]

TARGET_DOMAIN = "example.com"

For production, store this in a database or spreadsheet. Each row can represent one tracked combination:

keyword + city + country + language + device

That combination becomes your rank tracking unit.

Step 2: Send localized SERP API requests

Keep the request layer separate from parsing. That makes the workflow easier to maintain. View complete document>>

import os
import requests


SERP_API_KEY = os.getenv("TALORDATA_API_KEY")
SERP_ENDPOINT = os.getenv("TALORDATA_SERP_ENDPOINT")


def call_serp_api(payload):
    if not SERP_API_KEY:
        raise RuntimeError("Missing TALORDATA_API_KEY")

    headers = {
        "Authorization": f"Bearer {SERP_API_KEY}",
        "Content-Type": "application/json",
    }

    response = requests.post(
        SERP_ENDPOINT,
        json=payload,
        headers=headers,
        timeout=30,
    )

    response.raise_for_status()
    return response.json()


def fetch_local_serp(keyword, location_config):
    payload = {
        "engine": "google",
        "q": keyword,
        "gl": location_config["country"],
        "hl": location_config["language"],
        "location": location_config["location"],
        "device": location_config["device"],
        "num": 10,
        "output": "json",
    }

    return call_serp_api(payload)

The important part is not the exact variable name. It is that every request includes the local context.

Step 3: Extract organic results

SERP responses may use different field names depending on provider or search type, so the parser should be flexible.

from urllib.parse import urlparse


def clean_text(value):
    if not value:
        return ""
    return " ".join(str(value).split())


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

    parsed = urlparse(url)
    return parsed.netloc.replace("www.", "")


def get_organic_results(serp_json):
    return (
        serp_json.get("organic_results")
        or serp_json.get("organic")
        or serp_json.get("results")
        or []
    )


def normalize_organic_results(serp_json):
    rows = []

    for index, item in enumerate(get_organic_results(serp_json), start=1):
        url = item.get("link") or item.get("url") or ""

        rows.append({
            "position": item.get("position") or item.get("rank") or index,
            "title": clean_text(item.get("title")),
            "url": url,
            "domain": get_domain(url),
            "snippet": clean_text(item.get("snippet") or item.get("description")),
        })

    return rows

Now every city and language search returns the same clean structure.

Step 4: Match the target domain

The rank tracker needs to answer one question:

Did the target domain appear in this local SERP, and where?

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

    for item in results:
        domain = item["domain"].lower()

        if domain == target or domain.endswith("." + target):
            return {
                "position": item["position"],
                "matched_url": item["url"],
                "title": item["title"],
                "snippet": item["snippet"],
            }

    return {
        "position": None,
        "matched_url": "",
        "title": "",
        "snippet": "",
    }

Use None when the domain is not found. Do not quietly turn it into 99 or 100 unless your reporting system clearly defines that convention.

Step 5: Save local ranking snapshots

CSV is enough for the first version.

import csv
from datetime import datetime, timezone


CSV_COLUMNS = [
    "keyword",
    "target_domain",
    "city",
    "country",
    "language",
    "location",
    "device",
    "position",
    "matched_url",
    "title",
    "snippet",
    "collected_at",
]


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


def run_local_rank_tracking():
    collected_at = datetime.now(timezone.utc).isoformat()
    output_rows = []

    for keyword in KEYWORDS:
        for location_config in LOCATIONS:
            serp_json = fetch_local_serp(keyword, location_config)
            organic_results = normalize_organic_results(serp_json)
            ranking = find_target_ranking(organic_results, TARGET_DOMAIN)

            output_rows.append({
                "keyword": keyword,
                "target_domain": TARGET_DOMAIN,
                "city": location_config["city"],
                "country": location_config["country"],
                "language": location_config["language"],
                "location": location_config["location"],
                "device": location_config["device"],
                "position": ranking["position"],
                "matched_url": ranking["matched_url"],
                "title": ranking["title"],
                "snippet": ranking["snippet"],
                "collected_at": collected_at,
            })

    export_rankings(output_rows)


if __name__ == "__main__":
    run_local_rank_tracking()

Run this daily, and you have a local ranking history.

How to use the ranking data

Once you have snapshots, you can answer more useful questions.

Question

How to answer it

Which cities are weak?

Group by city and compare average position

Which pages rank locally?

Group by matched_url

Which language performs better?

Compare English, Spanish, French, or other language rows

Where did rankings drop?

Compare today’s snapshot with yesterday’s

Which keywords need local pages?

Find keywords with no ranking in target cities

Are mobile rankings different?

Compare desktop and mobile rows

This turns local SEO from guesswork into a repeatable reporting workflow.

When to use a database

CSV is fine for testing. Once the workflow runs regularly, use a database.

A simple table might include:

Column

Type

id

UUID or integer

keyword

Text

target_domain

Text

city

Text

country

Text

language

Text

device

Text

position

Integer or null

matched_url

Text

collected_at

Timestamp

From there, you can build local SEO dashboards, ranking alerts, city comparison reports, franchise visibility reports, or weekly SEO summaries.

Best practices

Track the same keyword and location combinations consistently. Changing your tracked set too often makes trends unreliable.

Store city, country, language, device, and timestamp with every row. Local ranking data without context is not useful.

Separate organic rankings from Maps, local pack, ads, and Shopping results. They belong in different reports.

Keep raw API responses during development. They help you debug parser issues and SERP feature changes.

Do not over-track stable keywords. Daily tracking is often enough for local SEO. High-frequency tracking should be reserved for important launches or volatile markets.

Use local language queries. Do not only translate the interface language. Search behavior can change with local phrasing.

FAQ

What is a SERP API for local SEO?

A SERP API for local SEO helps you collect search engine results programmatically by keyword, location, language, country, and device. This makes it easier to track local rankings at scale.

Why should I track rankings by city?

Google results can vary by city. A business may rank well in one market and poorly in another, even for the same keyword.

Why does language matter in local SEO tracking?

Search language affects query intent, result titles, snippets, and ranking pages. In multilingual markets, tracking only one language can hide important visibility gaps.

Should I track desktop and mobile separately?

Yes. Desktop and mobile SERPs can differ. Store device as a separate field so the report does not mix different result contexts.

Can this workflow track Google Maps rankings?

Yes, but Maps rankings should be parsed and reported separately from organic rankings. Local pack and Maps data answer different questions than standard web results.

Is CSV enough for local SEO rank tracking?

CSV works for a prototype or small report. For recurring tracking, alerts, historical trends, and dashboards, use a database.

Start your free trial of TalorData>>

Scale Your Data
Operations Today.

Join the world's most robust proxy network.

Start Free Trial