Proxies for Geo-Pricing Audits: Seeing the Price Each Region Sees
Run a geo pricing audit that holds up: noise baselines, a signal ladder to find what moves price, tax and FX normalisation, evidence and the right proxies.

A geo pricing audit that survives scrutiny needs more than a proxy in each country: it needs a noise baseline from several IPs inside the same country, a test that isolates which signal actually moves the price, and prices normalised for tax, currency and shipping before anyone calls a difference real.
Most "we checked from a VPN in Germany and it was cheaper" findings fall apart on the second look. The German price included 19% VAT and the US one did not, or the page was running an A/B test, or the site recognised a hosting IP and quietly served its default region. This guide is for pricing teams auditing competitors, procurement teams checking what suppliers charge elsewhere, channel teams watching resellers, and researchers or journalists investigating regional price differences on third-party sites.
If you are testing your own site's localised experience instead, our guide to proxies for localization testing covers that QA workflow.
The short answer
- Country-level exits are enough for nearly every pricing audit. City targeting matters only for local services, delivery zones and a few marketplaces.
- Run at least three exits per country at the same time, so you can tell a regional price from ordinary variation.
- Change one signal at a time. IP country, browser language, store cookie and account country each move prices on different sites. Knowing which one did it is the finding.
- Compare net of tax, in one currency, at a dated exchange rate. Otherwise VAT alone creates a fake 20% "difference".
- Keep a screenshot, the HTML and the verified exit IP for every flagged price.
- Check that the site believes your location. Some sites treat hosting IPs as VPN traffic and serve a default region, which hides real differences.
What a geo pricing audit is for
The same technique answers quite different questions. Decide which one you are asking, because it sets the targets, the sample and the evidence standard.
| Audit type | The question | Typical targets | Evidence standard |
|---|---|---|---|
| Competitive pricing | Where do competitors price lower or higher than us, and by how much? | Competitor stores, marketplaces | Internal decision support |
| SaaS and subscription pricing | Does this vendor charge less in other regions for the same plan? | Pricing pages, checkout flows | Procurement negotiation |
| Channel and reseller | Are partners or resellers undercutting agreed prices in some markets? | Reseller stores, marketplace listings | Contract enforcement |
| Travel and ticketing | Does the fare or ticket price change with the buyer's country? | Airlines, OTAs, event sites | Research or complaint |
| Consumer research and journalism | Are consumers in some countries systematically charged more? | Retail, apps, digital services | Publishable, reproducible |
The rest of this guide applies to all five. The difference is how much evidence you keep: a pricing analyst needs a clean table, a journalist needs something a lawyer will read.
Retail price collection at scale is covered in how ecommerce companies use proxies for competitive intelligence. This page is about the audit question specifically: is the price different because of where the buyer is?
Scraping at scale? Skip the blocks.
Fast, unblockable datacentre proxies with unlimited bandwidth.
Seven things that fake a regional price difference
Before any difference counts, rule these out.
| Confounder | What it looks like | How to rule it out |
|---|---|---|
| Tax display | EU and UK consumer prices usually include VAT; US prices usually exclude sales tax | Convert every price to net of tax before comparing |
| Currency conversion | A "cheaper" price that is just yesterday's exchange rate | Convert at one dated reference rate for the whole run |
| Shipping and fees | Free shipping in one market, a fee in another | Record product price, shipping and fees as separate fields |
| A/B price tests | Two prices in the same country on the same day | Measure within-country spread from several IPs |
| Personalisation | Returning-visitor discounts, cart-abandon offers | Fresh browser context, no cookies, logged out |
| Time and stock | Flash sales, dynamic pricing, sold-out variants | Fetch all countries in the same short window, record stock state |
| Default-region fallback | The site does not believe your IP and shows its home market | Check the page's currency and region indicator against the exit country |
The last one is the sneaky one because it produces false negatives. You run twelve countries, see identical prices, and conclude the site does not geo-price, when in fact it served you its default store twelve times.
Step one: measure the noise inside one country
Every audit should start with a control run. Pick one country, fetch the same product from several different exit IPs within a few minutes, and look at the spread.
If five UK exits return five identical prices, any cross-country gap is easy to interpret. If they return two different prices, the site is testing or personalising, and a single-IP check in each country would have produced nonsense.
The Scraping API takes a country_code per request, so a control run is a short loop. Each request gets its own rotating exit:
import re
import statistics
import requests
API = "https://scrape.sparkproxy.io/api/v1"
KEY = "YOUR_API_KEY"
PRODUCT = "https://store.example/product/sku-123"
def fetch_price(country):
r = requests.get(API, headers={"X-API-Key": KEY}, timeout=90, params={
"url": PRODUCT,
"render_js": "true",
"country_code": country,
})
r.raise_for_status()
# Site-specific: adapt the pattern to the target's price markup.
m = re.search(r'itemprop="price"\s+content="([\d.]+)"', r.text)
cur = re.search(r'itemprop="priceCurrency"\s+content="([A-Z]{3})"', r.text)
return (float(m.group(1)) if m else None, cur.group(1) if cur else None)
samples = [fetch_price("GB") for _ in range(5)]
prices = [p for p, _ in samples if p is not None]
print("currencies seen:", {c for _, c in samples})
print("min", min(prices), "max", max(prices), "median", statistics.median(prices))
A practical decision rule: only flag a regional difference when the gap between two countries' medians is larger than the widest within-country spread you observed in the control run. It is not a formal significance test, but it stops the most common false alarm cold.
Step two: the signal ladder
Sites decide "where is this buyer" from several inputs, and they do not all use the same ones. Change one at a time and record which change moved the price.
| Rung | Signal changed | Held constant | What a price change tells you |
|---|---|---|---|
| 1 | Exit IP country | Language, cookies, URL | Price follows IP geolocation |
| 2 | `Accept-Language` header | IP, cookies, URL | Price follows browser language, rare but real |
| 3 | Country or store path in the URL (`/de/`, `/uk/`) | IP, language | Separate regional storefronts with their own price lists |
| 4 | Store or currency selector cookie | IP, URL | Buyer can choose a region; price follows the choice |
| 5 | Device type | Everything else | Mobile and desktop priced differently |
| 6 | Logged-in account or billing country | Everything else | Price set at checkout by payment details, invisible to logged-out audits |
The rung that moves the price is often more important than the size of the gap. A price that follows IP alone means a buyer cannot easily see the other market's price. A price that follows a URL path any buyer can type is a different story, especially in the EU.
Rung 6 has a hard boundary. Logged-out audits cannot see prices that are set by billing country at checkout, and creating accounts or entering payment details to find them moves you from audit into terms of service and fraud-control territory. Note it as a limitation rather than working around it.
Headers for rung 2 can be passed through the API's forward_headers parameter. Device type for rung 5 is the device parameter.
Step three: normalise before you compare
Raw scraped prices are not comparable. Normalise in this order, and store every intermediate value so anyone can check the arithmetic.
- Split the price. Product price, shipping, other fees, and whether tax is included as displayed.
- Remove tax. Divide tax-inclusive prices by one plus the standard rate for that market. Note reduced rates where a product category qualifies.
- Convert currency at a single reference rate for the whole run. The European Central Bank publishes euro reference rates each working day, which gives you a dated, citable source.
- Index against a base market. Express every country as a percentage of your base, so 112 means 12% more expensive net of tax.
FX_TO_EUR = {"EUR": 1.0, "GBP": 1.17, "USD": 0.92} # example rates, date them
VAT = {"DE": 0.19, "FR": 0.20, "GB": 0.20, "US": 0.0} # US shown pre-sales-tax
def net_eur(price, currency, country, tax_included=True):
net = price / (1 + VAT[country]) if tax_included else price
return round(net * FX_TO_EUR[currency], 2)
base = net_eur(99.00, "EUR", "DE")
for country, price, cur in [("FR", 99.00, "EUR"), ("GB", 89.00, "GBP"), ("US", 99.00, "USD")]:
idx = net_eur(price, cur, country, tax_included=(country != "US")) / base * 100
print(country, round(idx))
The exchange rates in that snippet are example values, not current rates. Look them up for the date of your run.
Look at what the normalisation does to a headline. €99 in Germany and $99 in the US look identical. Net of German VAT and converted, the German price is about €83 and the US one about €91 before sales tax, so the US is roughly 10% more expensive on these example rates, the opposite of what the raw numbers suggested.
Capturing evidence that holds up
For internal pricing work, a normalised table is enough. For anything that might end up in a negotiation, a complaint or an article, keep a record per flagged observation:
- A full-page screenshot showing the price, currency and any region indicator.
- The raw HTML of the same response, so the price can be found in the source.
- The exit IP and its geolocation, checked in the same session against a geolocation service.
- A UTC timestamp and the request parameters: country, language, device, cookies.
- A SHA-256 hash of the screenshot and HTML, written to your log when captured.
Screenshots come from the same API call with format=screenshot, which the docs price as a +5 credit add-on. Take them only for flagged observations, not the whole run.
curl -G "https://scrape.sparkproxy.io/api/v1" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "url=https://store.example/product/sku-123" \
--data-urlencode "country_code=FR" \
--data-urlencode "format=screenshot" \
-o fr-sku-123.png && sha256sum fr-sku-123.png
Reproduce before you report
One capture is an observation. A finding is something that shows up again. Before a flagged gap leaves the analytics team, re-run just those products at least 24 hours later, from fresh exits, in the same countries, and at a similar time of day. Prices that reappear go in the report. Prices that do not were probably a test cell, a flash sale or a stock change, and belong in a footnote rather than a headline.
Write the method down alongside the results: countries, exits per country, the signals held constant, the tax rates and exchange rate date used, and the decision rule for flagging. If the company you are auditing disputes the finding, a reproducible method is what settles the argument, not the size of the gap.
Our IP geolocation accuracy guide explains why you verify the exit's location rather than trusting the label on the proxy.
Which proxies to use, and the hosting-IP trap
Geo pricing audits need correct country geolocation more than they need a particular IP type. Commercial geolocation databases generally place datacenter IPs in the right country, and a lot of retail, SaaS and travel sites price on that alone. The catch is the default-region fallback from the confounder table.
Some sites, especially streaming, digital goods and some travel sellers, flag hosting and VPN ranges and respond by serving their home region or blocking the page. For those, a datacenter exit gives you a wrong answer, not an error. Detect it on every run: if the page's currency or region indicator does not match the exit country, treat the observation as invalid, not as "no difference".
| Situation | Datacenter exits | Residential exits |
|---|---|---|
| Retail and SaaS pricing pages that price by IP country | Usually fine, verify currency per country | Not needed |
| Sites that serve a default region to hosting IPs | Wrong answer, detect and discard | Needed |
| Mobile app store or in-app pricing | Limited, app stores use account country | Also limited, account country dominates |
| Large volume, many SKUs, weekly rounds | Most cost-effective | Costly per GB |
SparkProxy is datacenter-first. Its proxy plans provide a USA pool and a worldwide rotating pool across 1M+ IPs in 80+ countries, which suits collection where you log the exit country per request. For choosing a specific country on each request, the Scraping API's country_code parameter is the direct route. SparkProxy does not sell residential proxy plans, so for targets that reject hosting IPs, buy residential exits elsewhere. For scraping jobs, the SparkProxy Scraping API does offer residential exits through its premium_proxy option, which routes a request through a residential pool for 10 credits, or 25 with JavaScript rendering. More on the difference in what geo-targeting means in proxies and residential vs datacenter proxies.
Whichever you use, never audit through a consumer VPN. A handful of shared exit IPs per country are exactly the ranges pricing and anti-fraud systems know best. See proxy vs VPN for the broader comparison.
Sample size and cost
An audit round is countries × products × exits per country. Here is an illustrative sizing, not a quote for any specific target.
Assumptions: 12 countries, 50 products, 3 exits per country, weekly rounds, pages need JavaScript rendering, screenshots for the 10% of observations that get flagged.
- Price fetches per round: 12 × 50 × 3 = 1,800.
- Credits per fetch per the API docs: 5 for rendering plus 5 for
country_code= 10, so 18,000 credits. - Screenshots: 180 flagged, as separate calls at 15 credits each (rendering, country and screenshot) = 2,700 credits.
- Per round: 20,700 credits. Four rounds a month: 82,800 credits.
That sits comfortably inside the Starter plan's 250,000 credits for $49/mo, with 50 concurrent requests, which leaves room for a control run and the signal-ladder tests. The 1,000 free credits cover a first control run on a few products before you pay anything.
The number that grows fastest is exits per country. Going from 3 to 10 improves your noise estimate but more than triples the bill, so run the heavier baseline once per target, then drop back to 3 for routine rounds unless the target shows within-country variation.
The legal backdrop, briefly
Geographic price differences are not illegal in general, and this is not legal advice. Two EU rules shape what an audit should look for.
The EU Geo-blocking Regulation (EU) 2018/302, applying since December 2018, does not require the same price in every member state. It stops traders from blocking or automatically redirecting customers from other member states without consent, and in defined situations requires that they can buy on the same general conditions as local customers. For an EU audit, "can a buyer in France reach the German store and buy at its terms?" is often the more useful question than "is the German price lower?". That is exactly what rung 3 of the signal ladder tests.
The Omnibus Directive (EU) 2019/2161 requires traders to tell consumers when a price has been personalised on the basis of automated decision-making, applying since May 2022. Personalised prices without that notice are a different finding from regional pricing, and your noise baseline is what separates them.
Outside the EU, rules vary by country and sector. Keep collection to public, logged-out pages, at polite request rates, and take advice before publishing findings that name companies.
Frequently asked questions
FAQ
A geo pricing audit checks whether a website shows different prices, fees or offers depending on the buyer's location. It fetches the same products from several countries at the same time, normalises for tax, currency and shipping, and records evidence of any regional differences that remain.
Not reliably. Consumer VPNs route many users through a few known exit IPs per country, so pricing and anti-fraud systems often recognise them and serve a default region or block the page. Use country-targeted proxies with several exits per country, and verify the site actually believes your location.
Often, yes. Many retail and SaaS sites price on IP geolocation, and datacenter IPs usually geolocate to the correct country. Some sites treat hosting IPs as VPN traffic and show their home market instead, so check the currency and region indicator on every page and use residential exits for those targets.
Use at least three exits per country at the same time. Run a heavier control run of five to ten exits in one country first: if those return different prices, the site is testing or personalising, and you should only flag cross-country gaps larger than that within-country spread.
Generally no. In the EU, the Geo-blocking Regulation lets prices differ between national storefronts but restricts blocking or redirecting buyers from other member states, and the Omnibus Directive requires disclosure of algorithmically personalised prices. Rules elsewhere vary, so get legal advice before publishing findings.
Split each price into product price, shipping and fees, remove VAT or sales tax, convert everything to one currency at a single dated reference rate such as the ECB euro rates, then index each country against a base market. Store every intermediate value so the comparison can be checked.
Get 20% off your first month
Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.
Save up to 15% more on quarterly, half-yearly and yearly plans
Related articles

Proxies for Fashion Resale Market Data
Resale market data across StockX, eBay, Vinted, Depop and luxury consignment: which proxies each marketplace needs, how to size collection and avoid bad data.

Proxies for Energy Tariff and Fuel Price Data
Fuel price data and energy tariff collection: which markets publish official open feeds, where proxies fill the gaps, and how to size and structure the scrape.

Proxies for Freelancers: Sizing and Billing Scraping Clients
Proxies for freelancers running scraping gigs: pass-through vs bundled pricing, splitting one plan across clients, billing credits per job and contract terms.
