← Back to Journal
· 4 min read

Same Keyword, Four Markets, Different Google Rankings: A Real Localization Test

A scraped rank number without a market pin is a rumor. We ran one keyword through the same SERP API four times, changing only gl/hl (US, UK, Germany, India) — and the top-10 shuffled. Real code, the full results table, what survived, what moved, and the rules that make rank data trustworthy. Plus the datacenter-IP caveat the API can't fix.

TL;DR

A rank position is not a property of a page — it's a property of a page in a market. We tested it: same keyword, same API, only gl/hl changed across US/UK/DE/IN, and only 3 of 14 domains held a stable top-10 everywhere; one domain sat at #5 in the US and #8 in India, another missed the US top-10 entirely but ranked #4 in India. Rules that fall out: pin gl/hl in every call, put them in the cache key, report the market with every number, and never compare runs across markets. Also: even a pinned API call is market-normalized — your exit IP still bends what real users see.

Every rank number you’ve ever scraped came with an invisible suffix: for somebody, somewhere. Strip that suffix and the number becomes a rumor — precise-looking, unfalsifiable, and useless for comparison.

We ran the experiment that proves it. One keyword, one API, four markets, everything else pinned. Here’s the code, the raw table, and the working rules that fall out.

The Setup

The tooling doesn’t matter — this is one API call per market through the cached SERP layer, with only gl/hl varying:

def serp(q, gl, hl):
    body = json.dumps({"q": q, "gl": gl, "hl": hl, "num": 10}).encode()
    req = urllib.request.Request(
        "https://google.serper.dev/search", data=body,
        headers={"X-API-KEY": os.environ["SERPER_API_KEY"],
                 "Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=20) as r:
        return [x["link"] for x in json.load(r).get("organic", [])]

kw = "python seo tools"
markets = [("us","en"), ("gb","en"), ("de","de"), ("in","en")]

Four calls, ~2 seconds each, one credit each. Then align the results into a position grid.

The Results

python seo tools, top-10 domain positions per market, one run each:

domain                              us    gb    de    in
www.reddit.com/r/TechSEO/...        1     1     1     1
www.python.org/success-stories      2     3     3     2
github.com/sethblack/python-seo     3     2     2     3
www.causalfunnel.com/blog/...       4     4     5     5
adver.tools/python/seo              5     6     4     8
sarataher.podia.com/...             6     5     6     6
www.youtube.com/watch?v=...         7     -     -     -
searchwilderness.com/free-seo...    8     7     7     9
serpapi.com/blog/python-seo-...     9     8     -     7
searchengineland.com/python-...     -     9     -     4
sourceforge.net/directory/seo       -     -     8     -
github.com/topics/seo-tools         -     -     9     -

(- = not in that market’s top-10.)

What the table actually says:

Three domains are universal. The Reddit thread, python.org, and the GitHub repo hold positions 1-3 in all four markets. Head-of-SERP reality is global — that’s why difficulty scoring treats top-3 occupancy by strong domains as the core signal.

Everything below position 3 is market-local. adver.tools runs 5 → 6 → 4 → 8 across the four markets. If your rank tracker reported “position 5” without a market label and last week’s run was US while this week’s was India, you just “detected” a drop that never happened.

Two domains exist only in some markets. searchengineland.com is absent from the US top-10 and sits at #4 in India. sourceforge and the GitHub topic page only surface in Germany. A “we rank top-10 for this keyword” claim is incomplete until you say where — being #4 in one market and invisible in another is normal.

Why This Happens

Localization isn’t Google translating results. It’s a ranking input:

  1. Geotargeted relevance. A page’s strength is partially market-specific — links, engagement, and freshness all evaluate differently per region.
  2. Language filtering. The de/de run rewards German-language pages and demotes English-only ones (and vice versa). That’s why hl matters as much as gl.
  3. Market-specific competition. The pool of pages trying to rank differs per market. India’s SEO content pool is not Germany’s.

And on top of the market layer there’s a personalization layer — IP, history, device — which the API normalizes away but real users still carry. Which leads to the caveat below.

The Datacenter Caveat

Pinning gl/hl makes your data reproducible. It does not make it identical to what a real user in that market sees.

We verified this the uncomfortable way while building Bing-powered volume tooling: SERPs scraped from our datacenter IP showed rankings that real users in the target market did not get. Same engine, same keyword, different vantage point. Your exit IP is itself a location signal, and a datacenter IP is a very unusual “user.”

The honest hierarchy:

  1. API + pinned gl/hl — reproducible, comparable, correct instrument for scoring and deltas. Use this for keyword difficulty.
  2. Same-market residential check — what actual users plausibly see; costs proxies or manual spot-checks; worth it before betting a quarter on a rank claim.
  3. Raw scrape from whatever IP — reproducible only by accident. Fine for experiments, wrong for decisions.

The Working Rules

Condensed from the table and the caveats:

  1. Pin gl/hl in every SERP call. Defaults are someone else’s inference about your intent.
  2. Put them in the cache key. A US response served for a German lookup is corruption dressed as efficiency — see the cache layer article.
  3. Report the market with every rank. “Position 5” is not a fact; “position 5, gl=us, hl=en” is.
  4. Compare like with like. Week-over-week deltas only count within the same market. Cross-market comparisons are content strategy questions (“should we target India?”), not tracking questions.
  5. Treat a rank as a sample, not a truth. One call per market per week is a trend line, not a measurement. If a number decides something expensive, re-run it.

Where This Is Wrong

  1. One keyword, one run, four markets. Variance between runs at the same market settings exists too — this test isolates the market axis, not the time axis. A proper variance study reruns each market N times.
  2. API-normalized SERPs. The instrument here is a SERP API, which smooths personalization by design. Raw browser SERPs likely vary more than this table shows, not less.
  3. num=10 only. Deep-pagination behavior (positions 11-100) can vary by market in ways this test didn’t touch.
  4. Four markets is not the world. The US/UK/DE/IN picks cover an English-axis split and one non-English market. CJK markets behave differently again — the reason our tooling has a --zh flag with its own gl=cn, hl=zh-CN defaults.

Fitting It Into a Workflow

  1. Acquire SERPs with pinned markets through one cached entry point
  2. Score difficulty per market — a keyword that’s easy in one market can be locked in another; SERP structure analysis is market-local too
  3. Check intent with the same pinned SERPs (intent classification)
  4. Track weekly per market, fresh calls, market-labeled output

The Point

“Averaging rank across markets” and “tracking rank without a market” are the same mistake wearing different hats. The table above is one keyword and four phone calls, and it already contains a #4-that-is-invisible, a 5-versus-8, and a top-3 that’s the only thing you can safely call global. Rank data becomes trustworthy the moment it stops pretending to be universal — pin the market, label the number, compare like with like.

FAQ

Do Google rankings differ by country for the same keyword?

Yes, measurably. In a controlled test — same keyword, same SERP API, only the gl/hl parameters changed — the top-10 overlapped but reordered across US, UK, Germany, and India. Only about 3 of 14 appearing domains held the same position band in every market; one domain varied from position 4 to 8, and two domains in the India top-10 didn't appear in the US top-10 at all. Localization is a ranking input, not a tiebreaker.

What are gl and hl parameters in SERP APIs?

gl is the country code Google uses to geolocate the search (e.g. us, gb, de, in) and hl is the interface language (e.g. en, de). Together they define which market's ranking you're sampling. Omitting them means the API or Google infers a market — often from your IP — which makes your data non-reproducible. Pin both in every call and store them in your cache key.

Can a SERP API give me the exact rankings a real user sees?

No, and any tool claiming otherwise is overselling. A SERP API returns a deterministic, market-pinned snapshot from its own infrastructure. It's the right instrument for scoring and comparison, but a real user's results additionally bend on their IP, search history, personalization, and device. Treat API ranks as a controlled sample of a distribution, not the distribution itself.

Why did my rank tracking show a big drop that real users don't see?

The three usual suspects: the market moved (a run with different gl/hl than last week's — compare like with like), the SERP changed shape (a PAA box, video carousel, or new competitor entered and pushed organic positions down without changing real-world traffic), or IP-level variance — datacenter exit IPs see slightly different results than residential users, a mismatch we've verified directly on Bing.

Want to run this analysis on your own site?

ZensInk Pro automates this pipeline. One command, from seed keywords to content plan.

Get Pro →