
City SERP divergence is the measurable difference in local results for the same query when the exit metro changes, holding the parser and headers constant.
That sentence is the unit this page exists to make operational. A country-US exit can return a perfectly healthy 200, a parseable organic list, and a local pack that describes a market nobody on your team sells into. The failure is silent: dashboards look green, rank history looks complete, and a franchise owner in Miami says those businesses have never been the ones that show. Country-level targeting explains the mechanism. This page is the measurement protocol — the basket you re-run, the columns you store, and the overlap formula you compute only after both metros assert clean. It is marked as a dataset so you can cite the method. It is not a substitute for a dated retrieval you actually performed.
[!NOTE] How to read this page. The 16-query basket, the column schema, and the Jaccard overlap formula are the citable artifacts. They are free to reuse with attribution to Aethyn and a link back here. This article does not publish first-party overlap percentages. We did not run a live Chicago-versus-Miami (or any other pair) measurement for this ship. Search layouts, local packs, and consent walls drift. Re-run before you cite a number — the same discipline as the HTTP 200 soft-block notes.
The practical job this protocol supports is not “scrape Google.” It is sampling the SERP a household in that metro actually sees, then comparing it to another metro under identical client conditions. Pair it with SERP tracking when the product is rank history, track rankings across countries when you are building the collector, and local business data when the object of interest is the listing card itself rather than the organic URL order.
Why country is the wrong unit for local intent
Local search is not a national catalog with a flag on top. The map pack, the “near me” ordering, and a large share of organic local results are computed from an approximate location — device GPS when the engine has it, otherwise the network’s idea of where the request came from. Two household exits two thousand miles apart, same query string, same User-Agent, same parser, routinely return different businesses, a different pack size, and a different organic order. That is not noise. That is the product.
Country targeting is the correct lever when the page does not vary sub-nationally: documentation, a global catalog SKU, a filing, most non-local informational queries. It is the wrong lever the moment you can name a location-sensitive mechanism — the pack, a store-picker, a media-market ad, a ZIP-gated hours sheet. If you cannot name the mechanism, stay on Premium country routing and spend the bandwidth on volume. If you can, Elite city suffixes are how you pin the sample. ISP suffixes are a narrower tool (carrier-shaped pages, CDN edges); they are not required for a standard local-pack study and they shrink the pool you draw from.
The silent-failure pattern is always the same. Status 200. Selectors hit. Rows land. The vantage point was never written down, so six weeks later nobody can tell whether last Tuesday’s Miami rank was actually Miami. The protocol below makes the vantage point a first-class column, not a comment in a runbook.
Protocol (run this, then cite your numbers)
Hold these constants across both metros: query string, device class (desktop), language, User-Agent, parser version, and collection window (same UTC day unless you are studying time-of-day). Change only the Elite city suffix and the sticky session id.
- Pin the city. Username shape:
aethyn-XXXXX-country-us-city-chicago-session-serpchi-lifetime-30. City targeting is Elite. Premium will refuse a city suffix rather than silently ignore it. Full suffix order lives in the targeting docs. - Verify the exit before the first SERP. Fetch
https://ipinfo.io/json(or equivalent) through the same session. Recordresolved_city,resolved_region,resolved_isp, andexit_ip. Ifresolved_citydoes not match what you asked for, rotate the session id and re-check, or keep the row and label it a country fallback. Never label a fallback row as Chicago. - Hold sticky for the flow. Lifetime is minutes, 1–1440, default 30. A SERP fetch plus a couple of
start=pages is usually well inside 30 minutes. Sticky is a hold on a rotating-pool residential IP, not a dedicated or static SKU. - Fetch with asserts. Status 200 is not success. Reject consent walls,
/sorry/interstitials, empty#rso, and deny titles before you write organic URLs. Soft 200s poison overlap math the same way they poison scraper datasets. - Parse into the schema below. Organic URLs in order, local-pack business names in order, ads present or not. Do not store reviewer identities.
- Repeat the other metro with a new session id and a new verify step. Do not reuse the Chicago sticky id for Miami.
- Compute overlap only on paired, assert-ok rows. Formula in the next section. If either side failed asserts, the pair does not exist.
HTTP 5499 (Elite) or SOCKS5 3499 for a browser-backed collector; Premium 2099 / 1099 is the country-level control leg, not the city sample. Same pool, both protocols.

The 16-query basket (citable)
Run every query in both metros. Keep the strings exact so someone else can reproduce the basket. Classes are labels for analysis, not a claim about how Google internally buckets them.
| ID | Query | Class |
|---|---|---|
| L01 | plumber near me | local_pack |
| L02 | emergency dentist | local_pack |
| L03 | 24 hour pharmacy | local_pack |
| L04 | locksmith open now | local_pack |
| L05 | veterinary clinic | local_pack |
| L06 | storage units | local_pack |
| L07 | car wash | local_pack |
| L08 | yoga studio | local_pack |
| C09 | italian restaurant | local_category |
| C10 | coffee shop wifi | local_category |
| C11 | best pizza | local_category |
| C12 | oil change | local_category |
| N13 | gmail | national_control |
| N14 | wikipedia | national_control |
| N15 | federal minimum wage | national_control |
| N16 | how to reset a router | national_control |
How to use the classes. Local-pack and local-category queries are where divergence is the object of study. National controls are a sanity check: if Gmail organic overlap between Chicago and Miami collapses, you probably changed a header, hit a consent wall, or parsed a soft block — not “city SEO.” Do not report a headline divergence number that mixes L/C with N.
Recommended first pair for a US study: Chicago and Miami (Elite -city-chicago / -city-miami). They are far enough apart that local inventory is not shared, and both are large enough that city suffixes usually resolve. They are examples, not a claim we measured them on this page. Substitute the metros you actually sell into; a 50-city grid is usually 45 cities of bandwidth you will not use.
Public web only. Respect robots.txt, rate limits, and Terms. This protocol is for sampling publicly rendered SERPs, not for circumventing logins or automating engagement.

Column schema (store the vantage point)
Every row is one query × one metro × one retrieval. Without the vantage columns, overlap is not auditable.
| Column | Type | Rule |
|---|---|---|
query_id | string | L01–N16 from the basket |
query_text | string | Exact string sent |
query_class | enum | local_pack / local_category / national_control |
requested_city | string | What you put in the username |
requested_country | string | ISO-ish, e.g. us |
resolved_city | string | From ipinfo (or equivalent), not the request |
resolved_region | string | State/region if present |
resolved_isp | string | org / ASN label |
exit_ip | string | The address you actually used |
collected_at | datetime | UTC ISO-8601 |
engine | string | e.g. google |
device | string | desktop unless you are studying mobile SERP |
sticky_session_id | string | Unique per metro pass |
sticky_ttl_min | int | 1–1440, default 30 |
http_status | int | Logged, not trusted |
body_bytes | int | Calibrate a per-route floor yourself |
assert_ok | bool | Parser saw real SERP structure, not a wall |
organic_urls | string[] | Ordered, normalized (strip tracking params) |
local_pack_names | string[] | Ordered business names; empty if no pack |
ads_present | bool | Top or side ads visible |
notes | string | CAPTCHA, consent, fallback, parser miss |
Overlap (compute after the run, never invent). For a paired query with both assert_ok = true:
- Organic Jaccard:
|set(organic_A) ∩ set(organic_B)| / |set(organic_A) ∪ set(organic_B)| - Pack Jaccard: same on
local_pack_names(case-fold, strip legal suffixes like LLC if you document that step) - Report median Jaccard per class, not a single blended number
- Drop pairs where either side failed asserts
- Publish
collected_atnext to the figure so a reader knows the retrieval date
If resolved city ≠ requested city, either exclude the pair or report it in a “fallback” slice. Mixing fallbacks into the city slice is how country-level data pretends to be metro-level data.

Sticky sessions, cookies, and ZIP alignment
Local results are not only “which IP.” Store pickers, ZIP cookies, and consent cookies persist on the client. If you rotate the exit mid-pagination, page 2 can be a different market than page 1 and the Jaccard is garbage. That is why the protocol uses a sticky session per metro pass, then a new session for the next metro.
Lifetime is in minutes (1–1440, default 30). Size it to the flow rather than the job: a ten-query basket with one page each is a few minutes; an hour of Maps zoom-and-scroll belongs on local business collection, not this SERP protocol. Extending TTL does not make the IP dedicated. When the window ends, the pool may hand you a different household in the same city — verify again if the job continues.
Do not pin ISP unless the hypothesis is carrier-shaped (a broadband offer page, a CDN edge). Extra constraints shrink availability. City is the lever for pack divergence; ISP is the lever for “through whom.”

What this page is not
It is not a ranked list of proxy vendors. It is not a Python tutorial (the SDK quickstart already covers username assembly). It is not a claim that Aethyn measured 37% pack overlap between two cities last Tuesday. If you need a number for a slide, run the basket, store the schema, and put your collected_at on the slide.
It is also not license to ignore rate limits. Residential exits look like households; they are not a reason to query like a botnet. Scale throughput by widening the city sample and pacing, not by hammering one sticky IP.
Common questions about this article
What is city SERP divergence?
Does this page publish Chicago versus Miami overlap percentages?
Why Elite city targeting instead of Premium country?
How long should the sticky session last?
Is this protocol free to cite?
Guides, integrations & docs
Continue reading

Country-Level Targeting Isn't Enough: City and ISP Proxies Explained
Country targeting fails silently. The page renders, the price parses, the row lands in your warehouse — describing a market nobody sells into. Here's when you need city or ISP granularity, when you don't, and how to prove the exit landed where you asked.

Residential Proxy Statistics 2026: Market Data & Network Trends
A data-driven look at the residential proxy market in 2026 — adoption, success rates, latency, pricing, and traffic patterns drawn from Aethyn network data and industry estimates.

Rotating vs Static Residential Proxies: How to Choose
Rotating residential proxies give you a new household IP per request. Sticky holds that IP for 1–1440 minutes (default 30) on the same rotating pool — not a static ISP product.
Sample the metro, not the country average
Elite city suffixes pin the exit. Sticky 1–1440 minutes (default 30) keep ZIP and store cookies aligned. Verify the city you landed in before you trust the rank.