Multi-Location Tracking

Track keyword rankings across different countries, states, and cities. Essential for local SEO, international businesses, and location-based services.

Overview

Search results vary significantly by location. A keyword ranking #1 in New York might be on page 3 in Los Angeles. The SerpWatch API lets you specify exact locations to get accurate, location-specific SERP data.

Location Targeting Options

Parameter Description Example
location_name Human-readable location name "New York, United States"
location_code Google Ads location code (numeric) 1023191 (New York City)

Location Codes

We recommend using location_code for precise targeting. Location codes correspond to Google Ads geo targets and provide the most accurate results. See Locations & Languages for a list of common codes.

Use Cases

  • Local SEO - Track rankings in specific cities for local businesses
  • Multi-Location Businesses - Monitor performance across franchise locations
  • International SEO - Compare rankings across different countries
  • Competitor Analysis - See how competitors rank in different markets
  • Location-Based Services - Optimize for "near me" and local intent queries

Basic Multi-Location Query

Track the same keyword across multiple locations by submitting separate requests for each location. Use the batch endpoint to submit all locations efficiently.

# Track "pizza delivery" in multiple US cities
curl -X POST "https://engine.v2.serpwatch.io/api/v2/serp/crawl/google/batch" \
  -H "Authorization: Bearer $SERPWATCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "keyword": "pizza delivery",
      "location_name": "New York, United States",
      "device": "mobile",
      "depth": 20
    },
    {
      "keyword": "pizza delivery",
      "location_name": "Los Angeles, United States",
      "device": "mobile",
      "depth": 20
    },
    {
      "keyword": "pizza delivery",
      "location_name": "Chicago, United States",
      "device": "mobile",
      "depth": 20
    }
  ]'
import requests
import os

API_KEY = os.environ.get("SERPWATCH_API_KEY")
BASE_URL = "https://engine.v2.serpwatch.io"

# Define target locations
locations = [
    {"name": "New York, United States", "code": 1023191},
    {"name": "Los Angeles, United States", "code": 1013962},
    {"name": "Chicago, United States", "code": 1016367},
    {"name": "Houston, United States", "code": 1026339},
    {"name": "Phoenix, United States", "code": 1013659},
]

keyword = "pizza delivery"

# Build batch request
batch = []
for loc in locations:
    batch.append({
        "keyword": keyword,
        "location_name": loc["name"],
        "location_code": loc["code"],
        "device": "mobile",
        "depth": 20,
        "language_code": "en"
    })

# Submit batch
response = requests.post(
    f"{BASE_URL}/api/v2/serp/crawl/google/batch",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json=batch
)

tasks = response.json()
print(f"Submitted {len(tasks)} location queries")

# Store task IDs for retrieval
task_map = {task["id"]: task["location_name"] for task in tasks}
const API_KEY = process.env.SERPWATCH_API_KEY;
const BASE_URL = "https://engine.v2.serpwatch.io";

// Define target locations
const locations = [
  { name: "New York, United States", code: 1023191 },
  { name: "Los Angeles, United States", code: 1013962 },
  { name: "Chicago, United States", code: 1016367 },
  { name: "Houston, United States", code: 1026339 },
  { name: "Phoenix, United States", code: 1013659 },
];

const keyword = "pizza delivery";

// Build batch request
const batch = locations.map(loc => ({
  keyword,
  location_name: loc.name,
  location_code: loc.code,
  device: "mobile",
  depth: 20,
  language_code: "en"
}));

// Submit batch
const response = await fetch(
  `${BASE_URL}/api/v2/serp/crawl/google/batch`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(batch)
  }
);

const tasks = await response.json();
console.log(`Submitted ${tasks.length} location queries`);

// Store task IDs for retrieval
const taskMap = new Map(tasks.map(t => [t.id, t.location_name]));

Common Location Codes

Here are frequently used location codes for the United States. For a complete list, see Locations & Languages.

US Major Cities

City Location Code Location Name
New York City 1023191 New York, New York, United States
Los Angeles 1013962 Los Angeles, California, United States
Chicago 1016367 Chicago, Illinois, United States
Houston 1026339 Houston, Texas, United States
Miami 1015116 Miami, Florida, United States
Seattle 1027744 Seattle, Washington, United States
Boston 1018127 Boston, Massachusetts, United States
San Francisco 1014221 San Francisco, California, United States

Countries

Country Location Code Language Code
United States 2840 en
United Kingdom 2826 en
Canada 2124 en or fr
Australia 2036 en
Germany 2276 de
France 2250 fr
Spain 2724 es
Brazil 2076 pt

Complete Multi-Location Workflow

Here's a complete example that tracks keywords across multiple locations and generates a comparison report.

#!/usr/bin/env python3
"""
Multi-Location Rank Tracker
Compares keyword rankings across different geographic locations
"""

import os
import time
import requests
from collections import defaultdict

API_KEY = os.environ.get("SERPWATCH_API_KEY")
BASE_URL = "https://engine.v2.serpwatch.io"

# Configuration
TARGET_DOMAIN = "yourdomain.com"
KEYWORDS = ["pizza delivery", "best pizza near me", "pizza restaurant"]
LOCATIONS = [
    {"name": "New York, United States", "code": 1023191},
    {"name": "Los Angeles, United States", "code": 1013962},
    {"name": "Chicago, United States", "code": 1016367},
]

def submit_batch(keywords, locations):
    """Submit batch request for all keyword/location combinations."""
    batch = []
    for keyword in keywords:
        for location in locations:
            batch.append({
                "keyword": keyword,
                "location_name": location["name"],
                "location_code": location["code"],
                "device": "mobile",
                "depth": 20,
                "language_code": "en"
            })

    response = requests.post(
        f"{BASE_URL}/api/v2/serp/crawl/google/batch",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        },
        json=batch
    )
    response.raise_for_status()
    return response.json()

def wait_for_results(tasks, timeout=300):
    """Wait for all tasks to complete."""
    results = {}
    pending = {t["id"] for t in tasks}
    start = time.time()

    while pending and (time.time() - start) < timeout:
        for task_id in list(pending):
            response = requests.get(
                f"{BASE_URL}/api/v2/serp/crawl/{task_id}",
                headers={"Authorization": f"Bearer {API_KEY}"}
            )
            data = response.json()

            if data["status"] in ["success", "completed", "error"]:
                results[task_id] = data
                pending.remove(task_id)

        if pending:
            print(f"  Waiting... {len(pending)} tasks remaining")
            time.sleep(5)

    return results

def find_rank(result, domain):
    """Find domain position in organic results."""
    for item in result.get("result", {}).get("organic", []):
        item_domain = item.get("domain", "").lower()
        if domain.lower() in item_domain:
            return item["position"]
    return None

def generate_report(tasks, results, domain):
    """Generate location comparison report."""
    # Organize by keyword -> location -> rank
    report = defaultdict(dict)

    for task in tasks:
        task_id = task["id"]
        keyword = task["keyword"]
        location = task["location_name"]
        result = results.get(task_id, {})

        if result.get("status") in ["success", "completed"]:
            rank = find_rank(result, domain)
            report[keyword][location] = rank
        else:
            report[keyword][location] = "Error"

    return report

def print_report(report, locations):
    """Print formatted comparison report."""
    # Header
    loc_names = [loc["name"].split(",")[0] for loc in locations]
    print("\n" + "=" * 80)
    print("MULTI-LOCATION RANKING REPORT")
    print("=" * 80)
    print(f"\nDomain: {TARGET_DOMAIN}\n")

    # Column headers
    header = f"{'Keyword':<30}"
    for name in loc_names:
        header += f"{name:>12}"
    print(header)
    print("-" * 80)

    # Data rows
    for keyword, ranks in report.items():
        row = f"{keyword[:28]:<30}"
        for loc in locations:
            rank = ranks.get(loc["name"])
            if rank is None:
                display = "Not found"
            elif rank == "Error":
                display = "Error"
            else:
                display = f"#{rank}"
            row += f"{display:>12}"
        print(row)

    print("-" * 80)

def main():
    total_queries = len(KEYWORDS) * len(LOCATIONS)
    print(f"Tracking {len(KEYWORDS)} keywords across {len(LOCATIONS)} locations")
    print(f"Total queries: {total_queries}")
    print("-" * 50)

    # Submit batch
    print("\nSubmitting batch request...")
    tasks = submit_batch(KEYWORDS, LOCATIONS)
    print(f"Submitted {len(tasks)} tasks")

    # Wait for results
    print("\nWaiting for results...")
    results = wait_for_results(tasks)
    print(f"Received {len(results)} results")

    # Generate report
    report = generate_report(tasks, results, TARGET_DOMAIN)
    print_report(report, LOCATIONS)

if __name__ == "__main__":
    main()

Best Practices

Optimize Query Efficiency

  • Use batch endpoints - Submit all location queries in a single batch request
  • Prioritize locations - Focus on your highest-value markets first
  • Set appropriate depth - Use lower depth (10-20) for local tracking where top positions matter most
  • Use location codes - More precise than location names and faster to process

Local SEO Considerations

  • Mobile-first - Use device: "mobile" for local searches as most are mobile
  • Check local pack - Look for your business in the local_pack results, not just organic
  • Monitor variations - Track both "pizza delivery" and "pizza delivery near me"
  • Track competitors - Include competitor domains in your analysis

Local Pack Results

For local businesses, the local pack (map results) often matters more than organic rankings. The SERP response includes a local_pack array with map listings. Check both organic and local pack positions for complete local SEO tracking.

International SEO Tips

  • Match language codes - Use the appropriate language_code for each country
  • Account for ccTLDs - Track your country-specific domains (example.de, example.fr)
  • Consider hreflang - Different URLs may rank in different countries
  • Time zone awareness - Schedule checks at appropriate local times

Credit Usage

Each location query consumes credits separately. Tracking 10 keywords across 5 locations uses 50 credits. Plan your location coverage based on your credit budget and business priorities.