Skip to content

SERP tracking by country

Search results differ by country. Check rankings from an exit IP in each market you track, at a steady, modest rate, and keep the history.

Proxy URL, exit IP in United Kingdom

http://USERNAME-cc-GB:PASSWORD@scale-eu.netdash.io:2000

Residential exit IPs in United Kingdom

Your clientGateway -eu
Your clientGateway -eu
Rotating: requests from your client go through the -eu gateway, which assigns exit IPs in United Kingdom for you.

Illustrative routes, not live traffic. Exits are available in most countries; the countries shown are examples.

  • Country-level targeting by ISO code
  • Mobile exits on the same syntax
  • Rotating sessions for independent queries

SERP tracking proxies for international rankings

A rank check that runs from your office or a cloud server shows the results for that location. If you report rankings for several countries, each query has to come from the market it measures. SERP tracking proxies give every query an exit IP in the country you name, so the results page you parse is the version served to that country, not to your data center.

With netdash, the country is part of the proxy username: USERNAME-cc-DE for Germany, USERNAME-cc-GB for the United Kingdom. One username and password work on every residential and mobile plan, so the same code covers desktop and mobile checks.

What changes between markets

Three things decide which results page you get. The exit country decides the market. The search engine’s own language and region settings, where it offers them, decide the interface language. The device type changes the layout and sometimes the order of results, so desktop and mobile rankings are tracked separately.

Targeting is country-level. You can’t pick a city or region, so city-level local results are out of scope and the results you collect reflect the country as a whole. The locations page shows both gateways, and country targeting explains how to choose the regional gateway for an exit country.

  • Exit country: -cc- plus an ISO 3166-1 alpha-2 code, GB not UK
  • Interface language: the engine’s language setting and an Accept-Language header
  • Device: a desktop client, or a mobile user agent and viewport on the Mobile plan
  • Locations below country level: not available

Before you build

Many search engines restrict automated queries in their terms and offer official APIs or licensed data. Check the terms and robots.txt of each engine you query and use an official option where it fits. Keep the rate low and the volume to what your report needs. netdash does not solve CAPTCHAs: if an engine challenges your requests, slow down or stop. See how we expect netdash to be used.

A daily rank check, step by step

  1. Step 1: Define the matrix

    Keywords × markets × devices, and the domain whose position you track. Each combination is one row.

  2. Step 2: Check the engine’s terms

    Use an official API or licensed data where it covers your needs. Query results pages only where the terms allow it.

  3. Step 3: Configure each row

    Exit country via -cc-, the engine’s language setting, and a desktop or mobile client.

    Country targeting
  4. Step 4: Query at a steady rate

    One results page per keyword per day, spread over the day, on rotating sessions. No bursts.

  5. Step 5: Parse and store

    The position of your domain, the URL that ranks, market, device and date.

  6. Step 6: Report movement

    Day-over-day changes per market, with an alert when a tracked position falls.

Keyword × market × device matrix

One row per market and device. The examples use the Scale plan: every residential plan uses the same syntax, and you switch plans by changing the host and port. Every row uses the same proxy password from the dashboard; the proxy string format explains each part.

Desktop, United States

scale-us.netdash.io:2000 · USERNAME-cc-US

Rotating, through the Americas gateway.

Desktop, Germany

scale-eu.netdash.io:2000 · USERNAME-cc-DE

Rotating, through the Europe gateway.

Desktop, United Kingdom

scale-eu.netdash.io:2000 · USERNAME-cc-GB

Rotating. Use GB, not UK.

Mobile, United Kingdom

mobile-eu.netdash.io:4000 · USERNAME-cc-GB

Rotating, with a mobile user agent and viewport set in your client.

Session mode

Rotating: no session parameters

The exit IP isn’t pinned. Requests sent through one HTTPS tunnel share that tunnel’s exit IP.

More markets

One row per ISO country code

To confirm availability for a specific country, contact support.

Generate the settings for one market

Change the country, plan or gateway and copy the result into your rank checker’s configuration.

Your route

Through the AshburnFrankfurt gateway to an exit IP in Via Ashburn (-us)United StatesCanadaMexicoBrazilArgentinaChileVia Frankfurt (-eu)United KingdomGermanyFranceSouth AfricaIndiaJapan, rotatingsticky for 15 minsticky for 30 minsticky for 12 hsticky for 7 days, on the SolveScaleOpus plan.

Proxy URL

http://USERNAME-cc-GB:PASSWORD@scale-eu.netdash.io:2000

Replace USERNAME and PASSWORD with the credentials from your dashboard.

Scale, Europe gateway, exit IP in United Kingdom, rotating.

curl --max-time 30 -x "http://USERNAME-cc-GB:PASSWORD@scale-eu.netdash.io:2000" \
  "https://api.ipify.org?format=json"
import requests

proxy = "http://USERNAME-cc-GB:PASSWORD@scale-eu.netdash.io:2000"
proxies = {"http": proxy, "https": proxy}  # set both, or HTTPS requests bypass the proxy

response = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=30)
response.raise_for_status()
print(response.text)
import { ProxyAgent, fetch } from "undici";

const dispatcher = new ProxyAgent({
  uri: "http://scale-eu.netdash.io:2000",
  token: "Basic " + Buffer.from("USERNAME-cc-GB:PASSWORD").toString("base64"),
});

const res = await fetch("https://api.ipify.org?format=json", { dispatcher, signal: AbortSignal.timeout(30_000) });
console.log(await res.text());
import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: {
    server: "http://scale-eu.netdash.io:2000",
    username: "USERNAME-cc-GB",
    password: "PASSWORD",
  },
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org?format=json");
console.log(await page.textContent("body"));
await browser.close();
{"ip":"203.0.113.10"}

Your output shows a different address in the country you chose.

Illustrative routes, not live traffic. Exits are available in most countries; the countries shown are examples. For exits in Oceania, ask support which gateway to use.All locations

Finding your position in a results page

One httpx.Client per query, with the exit country in the username and no session parameters, so every query stands alone. position() returns the 1-based rank and URL of the first result on your domain. The parser here only collects links in page order: replace it with one that reads the organic results of the page you query.

The search URL is an example. Point it at a source whose terms allow automated queries, or use the engine’s official API. httpx 0.28 and later take proxy=; the older proxies= argument was removed.

  • A fixed pause between queries; a 403 or 429 stops the run instead of retrying
  • The password is URL-encoded because it sits inside the proxy URL
  • Results that need JavaScript: render them with Playwright
  • More on clients in the Python guide and the Go guide
import json
import os
import time
from datetime import date
from html.parser import HTMLParser
from urllib.parse import quote, quote_plus, urlparse

import httpx

USER = os.environ["NETDASH_USERNAME"]
PASS = quote(os.environ["NETDASH_PASSWORD"], safe="")  # encoded: it sits inside a URL

# Market -> gateway and Accept-Language. US pairs with the Americas gateway, DE and GB with Europe.
MARKETS = {
    "US": ("scale-us.netdash.io:2000", "en-US,en;q=0.8"),
    "DE": ("scale-eu.netdash.io:2000", "de-DE,de;q=0.8"),
    "GB": ("scale-eu.netdash.io:2000", "en-GB,en;q=0.8"),
}
SEARCH_URL = "https://search.example/search?q={query}"  # example host: use a source that allows it
DOMAIN = "yourdomain.example"
KEYWORDS = ["project management software", "invoice template"]
PAUSE_SECONDS = 90  # a steady pace; spread the daily run over the day


class ResultLinks(HTMLParser):
    """Collects absolute links in page order. Replace with a parser for your results page."""

    def __init__(self):
        super().__init__()
        self.links = []

    def handle_starttag(self, tag, attrs):
        href = dict(attrs).get("href") or ""
        if tag == "a" and href.startswith("http"):
            self.links.append(href)


def client_for(cc):
    host, lang = MARKETS[cc]
    # Rotating: no session parameters, so every query stands alone.
    proxy = f"http://{USER}-cc-{cc}:{PASS}@{host}"
    return httpx.Client(proxy=proxy, timeout=30, headers={"Accept-Language": lang})


def position(html, domain):
    """1-based position and URL of the first result on `domain`, or (None, None)."""
    parser = ResultLinks()
    parser.feed(html)
    for rank, link in enumerate(parser.links, start=1):
        host = urlparse(link).hostname or ""
        if host == domain or host.endswith("." + domain):
            return rank, link
    return None, None


for keyword in KEYWORDS:
    for cc in MARKETS:
        with client_for(cc) as client:
            # A wrong username or password fails here with httpx.ProxyError (407).
            response = client.get(SEARCH_URL.format(query=quote_plus(keyword)))
        if response.status_code in (403, 429):
            raise SystemExit(f"{cc}: HTTP {response.status_code}. Stop, then review volume and terms.")
        response.raise_for_status()
        rank, url = position(response.text, DOMAIN)
        print(json.dumps({"date": date.today().isoformat(), "market": cc, "device": "desktop",
                          "keyword": keyword, "position": rank, "url": url}))
        time.sleep(PAUSE_SECONDS)
Python

Check your own international SEO

The same exits work for your own site. Request your pages from each market and confirm that visitors get the right country version, that your hreflang alternates point where you expect, and that geo-redirects don’t send people who chose another country version back where they started.

For a full test setup with assertions per country, see localization testing. To check how your own ads and landing pages render per market, see ad verification.

Which product to query from

  • Residential proxies

    Self-serve

    Desktop results per country. Each query is independent, so rotating sessions fit. The examples use the Scale plan.

    From $1.20/GB, graduated per order

  • Mobile proxies

    Self-serve

    Mobile results from mobile exit IPs, paired with a mobile user agent and viewport in your client. Carrier, city and network generation can’t be selected.

    From $3.50/GB, graduated per order

SERP tracking questions

No. Targeting is country-level, so the results you collect reflect the country, not a city or region.

Many restrict automated queries in their terms and offer official APIs or licensed data instead. Check the terms of each engine before you build and use an official option where it fits.

Whether you can collect a given dataset depends on the terms, the data and your jurisdiction; get legal advice for your case.

Rotating. Each query stands alone, so leave out the session parameters and the exit IP isn’t pinned. Requests sent through one HTTPS tunnel share that tunnel’s exit IP; when consecutive requests must share an exit IP, use a sticky session instead.

netdash does not solve CAPTCHAs or other challenges, and your code shouldn’t try to get past them. Slow down, reduce volume, or switch to an official data source.

Use the Mobile plan (mobile-eu.netdash.io:4000 or mobile-us.netdash.io:4000) for mobile exit IPs, and set a mobile user agent and viewport in your client. Credentials and username syntax are the same as on the residential plans.

Track your first market

Run one keyword in one market first and check the parsed position by hand, then build out the matrix. One username and password cover the Scale and Mobile plans.

Privacy choices

We use cookies and similar technologies that this site needs to work and to remember your choice. We also use Google Analytics to understand which pages and features people use: in the EEA, the UK and Switzerland only with your consent. Live chat loads only if you open it. You can change your choice at any time under “Privacy choices” in the footer. Privacy Policy

Privacy choices

Choose what netdash.io may use. You can change this at any time under “Privacy choices” in the footer.

Necessary (always on)

Keeps the site working and secure, and remembers your privacy choices (nd_consent, 12 months).

In the EEA, the UK and Switzerland it stays off until you switch it on; elsewhere it is on unless you switch it off here. Google Analytics 4 measures page views, clicks on links and buttons, use of the price calculator, connection builder and code examples, scroll depth, file downloads, and page-performance metrics. It sets the cookies _ga and _ga_96MPNM6GHH, which last up to 12 months. Google processes this data, which may involve transfer to the United States. We do not send your name, email address or account details to Google Analytics. With this switched on we also remember a referral code or campaign you arrived with (nd_attr, 30 days), so a partner can be credited if you sign up.

Support chat (loads only when you open it)

Our live chat is provided by LiveChat. It loads only when you open the chat window. LiveChat then sets its own cookies and processes the messages you send and technical data about your browser.