← Back to blog

Your Proxy's Location Is Not Your Search Location

Here's a setup that looks correct and usually isn't. You need to know how a page ranks in Paris, so you route the request through a residential proxy with a French exit IP. The response comes back in French, with .fr domains near the top and French brands in the results. It looks exactly like what you expected, so nobody checks it again.

But you're not seeing what a Paris searcher sees. You're seeing what Google shows a request it believes came from somewhere in France, which is a much weaker statement — and for a large class of queries, a materially different page.

The underlying problem is that geography in Google search isn't one setting. It's four independent layers, and most scrapers control exactly one of them.

Layer 1: the exit IP is a hint, not an instruction

Google infers an approximate location from the request's IP address, but treats it as a signal to be weighed rather than a fact to be obeyed. Explicit location signals in the request override it.

There's also a more mundane problem underneath the first one: IP geolocation is unreliable below the country level. Residential proxy pools inherit whatever the geolocation databases claim about each address, and those claims are frequently stale or simply wrong at city granularity — an address advertised as Lyon may resolve to Paris, or to nothing more specific than "France." Country-level accuracy is generally solid. City-level accuracy, which is what rank tracking actually needs, is not something an IP alone reliably buys you. This is worth keeping in mind when sizing a proxy strategy — see our guide to rotating proxies for what proxies are genuinely good at, which is IP reputation and request distribution, not precise geography.

Layer 2: gl boosts, it doesn't filter

The gl parameter sets the result country. The near-universal misconception is that gl=fr means "give me French results." It doesn't. It means results matching that country get boosted in ranking, not strictly filtered from the set.

That distinction has real consequences for anyone comparing numbers. A domain that's absent from your gl=fr check can still appear for actual French searchers, and international domains you didn't expect will still show up in a country-targeted request because they were never excluded — only down-weighted. If you're treating gl as a filter and reporting the result as "rankings in France," you're reporting something narrower than what you measured.

Layer 3: hl does two jobs, and people conflate them

hl sets the interface language. It also influences which results get selected for international queries, which is the part that causes trouble, because it makes hl look like a geography control when it isn't.

Language and country are orthogonal. A French speaker in Montreal and a French speaker in Paris get meaningfully different results for the same query in the same language. Setting hl=fr and calling it French targeting conflates two decisions that should be made separately — and if you only set hl, you've specified the language while leaving the geography to be decided by whatever your infrastructure happened to do.

Layer 4: the geo-resolver, and the only city-level lever

The signal that actually moves Google's location resolution at city granularity is UULE — a Base64-encoded location value that overrides the IP-inferred location. It's the layer that changes the local pack. It's also undocumented: it's been stable for roughly a decade, which is why every SERP tool depends on it, but Google has never committed to it, and a change to their pipeline could deprecate it without notice.

This is the layer most homegrown scrapers skip entirely, because it isn't discoverable from Google's own documentation. It's also the one that matters most for exactly the queries people build rank tracking to watch.

The local pack is where a broken location stack shows up first

Here's why the mistake survives so long: broad informational queries barely move between cities in the same country. You can have your location stack completely misconfigured and get results that look right, because for best project management software there's very little for the geography to change.

Then there's everything else. Queries with local intent — services, retail, restaurants, anything phrased as "near me" — swing hard between cities. The local results block is driven almost entirely by the resolved location, and shopping results shift with it too. If you want to know whether your location targeting actually works, test it with a query that has local intent. Testing with a broad informational query proves nothing, which is precisely why the setup at the top of this post goes unquestioned for so long.

The failure mode is silence

Getting blocked is at least loud. You get a 403, a CAPTCHA, an empty page — something you can alert on, and our guide to avoiding blocks exists because that failure announces itself.

Wrong location targeting announces nothing. You get a 200 and a full page of entirely plausible results. Run the same query under three different location configurations and you get three different answers, all successful, with nothing in any response indicating which one corresponds to what a real searcher sees. The discrepancy typically surfaces months later, when someone compares your rank report against what they see on their own phone.

The same silence undermines historical data. A rank number recorded last quarter means nothing unless you also recorded every layer that produced it. If any layer was inherited from infrastructure rather than set explicitly — the proxy pool's exit region, a default country, a datacenter's location — then it may have changed since, and you have no way to tell whether a movement in the chart is a ranking change or a targeting change.

Pin every layer explicitly

The rule that follows from all of this: any layer you don't set is being set for you. Not by nothing — by your proxy pool, your datacenter region, or somebody's default. Explicit beats inherited on all four.

// The same query asking three different questions
{ "query": "coffee roasters", "gl": "fr", "hl": "fr" }

{ "query": "coffee roasters", "gl": "fr", "hl": "fr",
  "location": "Paris,Ile-de-France,France" }

{ "query": "coffee roasters", "gl": "fr", "hl": "fr",
  "coordinates": { "latitude": 48.8566, "longitude": 2.3522 } }

The first asks for country-weighted results with a French interface and no city resolution. The second resolves to a specific city. The third resolves to a point. All three succeed, and all three return different local results — so the one you pick needs to be a deliberate choice recorded alongside the data, not a side effect of how the request happened to be routed.

In practice that means: decide language and country separately, use a stable location identifier rather than a hand-encoded blob, store the full parameter set with each result so a historical number can be re-derived, and keep the choice consistent for as long as you intend to compare numbers over time.

How PrismCrawl handles the location stack

PrismCrawl exposes these as explicit parameters on the Google search endpoint rather than leaving them to infrastructure:

  • location takes an Active Canonical Name — "Austin,Texas,United States" — from a pinned Google geo-target dataset, currently version 2026-07-16. Pinning matters for the reproducibility problem above: the identifier you stored last quarter still refers to the same place, which isn't guaranteed when you're encoding location values by hand.
  • coordinates takes a latitude and longitude for point precision. It's mutually exclusive with location — you're picking one way to answer the question, not stacking two.
  • gl covers over 250 country codes and hl covers more than 70 interface languages, kept as the separate decisions they are.
  • search_parameters echoes every parameter back on the response, nulled where you didn't set one. That's the replay property this whole post argues for: the record of what you asked travels with the answer, so a stored result stays interpretable.

One scoping note: city-level targeting is Google-only. The Bing endpoint uses cc and setlang for country and language and has no location or coordinates equivalent, so don't assume parity across engines when you're building comparisons.

If you're building geo-sensitive tracking, this is worth testing against real data before committing — the free tier is 25 credits with no credit card, which is enough to run the same query under several location configurations and see for yourself how far apart the answers land. Full parameter details are in the API reference, and there's more on the surrounding architecture in our SEO rank tracker guide.

Frequently asked questions

Does a residential proxy in a city give me that city's search results?

Not reliably. Google treats the exit IP as one location signal among several, and it can be overridden by explicit signals in the request. IP geolocation is also frequently wrong below the country level, so a proxy advertised as being in one city often resolves somewhere else entirely. You get roughly country-level accuracy, not the city-level targeting most rank tracking assumes.

What is the difference between gl and hl?

gl is a result-country signal and hl is an interface-language signal. gl boosts results matching that country rather than strictly filtering to it, so you still see results from elsewhere, just re-weighted. hl sets the language of the interface and also influences which results get selected for international queries. They are independent: French-language results and results targeted at France are not the same set.

Do I need city-level location targeting, or is country enough?

It depends entirely on the query's intent. Broad informational queries barely move between cities in the same country, so country-level targeting is usually fine. Anything with local intent — services, retail, "near me" phrasing — swings hard between cities, and the local pack in particular is driven almost entirely by the resolved location. If you are tracking those, country-level targeting will produce plausible-looking results that do not match what a real searcher sees.