Compare a Product Page Across Locations
Retail sites often change what they show based on where the request comes from. That is useful when you are checking localized messaging, market selectors, shipping copy, or regional pricing — but it is hard to do from a single office IP.
This example uses the Scraper API to fetch the same product URL twice: once through a United States residential proxy, and once through a United Kingdom residential proxy. Then we compare the Markdown.
What you will get
By the end, you will have:
- Two Markdown extracts of the same Nike product page
- Confirmed
metadata.proxy.countryvalues for each run - A simple side-by-side check for location-specific content
Target page
https://www.nike.com/t/air-force-1-07-mens-shoes-jBrhbr/CW2288-111Why this page works well for a demo:
- It is a real public product detail page
- It responds differently by exit country
- In our captured UK run, Nike surfaced an explicit location banner
Setup
You need a Geonode API key and Python with requests installed.
pip install requests
export GEONODE_SCRAPER_API_KEY="YOUR_API_KEY"PowerShell:
pip install requests
$env:GEONODE_SCRAPER_API_KEY="YOUR_API_KEY"Keep your API key private
Do not hardcode the key in source or commit it to git. Use an environment variable or a local .env file.
The idea
Keep everything identical except the country:
{
"url": "https://www.nike.com/t/air-force-1-07-mens-shoes-jBrhbr/CW2288-111",
"formats": ["markdown"],
"render_js": true,
"processing_mode": "sync",
"proxy": {
"country": "US",
"type": "residential"
}
}Then repeat the same request with "country": "GB".
That isolates geo-targeting as the only intentional variable.
| Field | Why it is set |
|---|---|
formats: ["markdown"] | Easy to read and compare |
render_js: true | Nike's PDP is JS-heavy |
processing_mode: sync | Wait for the full result in one response |
proxy.country | Force US or GB exit |
proxy.type: residential | Better fit for consumer retail sites |
Implementation
import json
import os
from pathlib import Path
import requests
API_KEY = os.environ["GEONODE_SCRAPER_API_KEY"]
URL = "https://www.nike.com/t/air-force-1-07-mens-shoes-jBrhbr/CW2288-111"
COUNTRIES = ["US", "GB"]
OUT = Path("output")
OUT.mkdir(exist_ok=True)
def extract(country: str) -> dict:
response = requests.post(
"https://scraper.geonode.io/v1/extract",
headers={
"X-Api-Key": API_KEY,
"Content-Type": "application/json",
},
json={
"url": URL,
"formats": ["markdown"],
"render_js": True,
"processing_mode": "sync",
"proxy": {
"country": country,
"type": "residential",
},
},
timeout=300,
)
response.raise_for_status()
return response.json()
results = {}
for country in COUNTRIES:
result = extract(country)
markdown = result["data"]["markdown"]
path = OUT / f"nike_af1_{country}.md"
path.write_text(markdown, encoding="utf-8")
results[country] = {
"proxy": result["metadata"]["proxy"],
"tokens_charged": result["tokens_charged"],
"markdown_length": len(markdown),
"has_uk_banner": "We think you are in United Kingdom" in markdown,
"path": str(path),
}
print(country, results[country])
us = (OUT / "nike_af1_US.md").read_text(encoding="utf-8")
gb = (OUT / "nike_af1_GB.md").read_text(encoding="utf-8")
summary = {
"target_url": URL,
"markdown_differs": us != gb,
"results": results,
}
(OUT / "geo_comparison.json").write_text(
json.dumps(summary, indent=2),
encoding="utf-8",
)
print("markdown_differs:", summary["markdown_differs"])Run it:
python geo_targeted_extract.pyYou should get:
output/nike_af1_US.mdoutput/nike_af1_GB.mdoutput/geo_comparison.json
What you get in the response
A successful sync extract looks like this shape:
{
"data": {
"markdown": "# Nike Air Force 1 '07\n\n$115\n..."
},
"metadata": {
"url": "https://www.nike.com/t/air-force-1-07-mens-shoes-jBrhbr/CW2288-111",
"render_js": true,
"http_status": 200,
"duration_ms": 9069,
"formats": ["markdown"],
"proxy": {
"country": "US",
"type": "residential"
},
"processing_mode": "sync",
"headers": {},
"wait_config": null
},
"tokens_charged": 1
}You mainly care about:
| Field | Meaning |
|---|---|
data.markdown | Clean page content for that country |
metadata.proxy | Country and proxy type actually used |
metadata.http_status | Status from the target site |
metadata.duration_ms | How long the extract took |
tokens_charged | Tokens billed for the request |
You do not get structured product fields such as name, price, or sku. Those must be parsed from data.markdown if you need them.
Captured US vs GB results
From the live runs used for this example:
| US | GB | |
|---|---|---|
| HTTP status | 200 | 200 |
tokens_charged | 1 | 1 |
metadata.proxy.country | US | GB |
metadata.proxy.type | residential | residential |
metadata.render_js | true | true |
metadata.duration_ms | 9069 | 13864 |
| Markdown length | 67817 | 42453 |
| Markdown differs | yes | yes |
Both returned the product:
# Nike Air Force 1 '07
$115The UK extract also included this location banner:
# We think you are in United Kingdom. Update your location?That banner was not present in the captured US extract.
Read the result carefully
Geo-targeting does not guarantee a currency change. In this captured run, both markets still showed $115. The useful proof is that the page reacted to exit country — here via Nike's location banner and different Markdown overall.
How to judge success
- Proxy applied —
metadata.proxy.countrymatches what you requested - Content extracted —
data.markdowncontains the product page - Meaningful difference — the two Markdown files differ, or one country shows a clear market signal
Do not judge only on price. Banners, market pickers, shipping text, and locale strings all count.
When to use this pattern
Use geo-targeted extract when location changes the page you care about:
- Market localization QA
- Competitor monitoring by region
- Shipping / availability messaging checks
- Detecting country-specific offers or banners
If location does not matter, omit proxy.country and let the API use default routing.
Next
Once this pattern is working, natural follow-ons are:
- Batch-extract a list of SKUs from one country
- Map a storefront, filter product URLs, then batch-extract them
- Combine
proxy.countrywith custom headers such asAccept-Language