Use this when localization, pricing, or ad delivery differs by market and you need to see each variant the way a local user does. Each session opens with residential egress pinned to a country, so the target serves the real regional experience.
Executable starters
geo-check.ts
const API_BASE = 'https://api.browser.city/v1';const apiKey = process.env.BROWSERCITY_API_KEY;if (!apiKey) { throw new Error('Set BROWSERCITY_API_KEY before running this script.');}async function callAction<T>(action: string, body: unknown): Promise<T> { const response = await fetch(`${API_BASE}/do/${action}`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); if (!response.ok) { throw new Error(`${action} failed: ${response.status} ${await response.text()}`); } return (await response.json()) as T;}type OpenResponse = { result: { sessionId: string } };type MarkdownResponse = { result: string };for (const country of ['US', 'DE', 'JP']) { const opened = await callAction<OpenResponse>('open', { browser: 'chromium', labels: ['template:geo-check'], egress: { mode: 'managed', proxyType: 'residential', country }, }); try { await callAction('navigate', { sessionId: opened.result.sessionId, url: 'https://example.com/pricing', }); const page = await callAction<MarkdownResponse>('markdown', { sessionId: opened.result.sessionId, }); console.log(JSON.stringify({ country, markdown: page.result })); } finally { await callAction('close', { sessionId: opened.result.sessionId }); }}import jsonimport osimport urllib.errorimport urllib.requestAPI_BASE = "https://api.browser.city/v1"API_KEY = os.environ["BROWSERCITY_API_KEY"]def call_action(action, body): request = urllib.request.Request( f"{API_BASE}/do/{action}", data=json.dumps(body).encode("utf-8"), headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", }, method="POST", ) try: with urllib.request.urlopen(request, timeout=60) as response: return json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as error: detail = error.read().decode("utf-8", errors="replace") raise RuntimeError(f"{action} failed: {error.code} {detail}") from errorfor country in ["US", "DE", "JP"]: opened = call_action( "open", { "browser": "chromium", "labels": ["template:geo-check"], "egress": {"mode": "managed", "proxyType": "residential", "country": country}, }, ) try: call_action( "navigate", {"sessionId": opened["result"]["sessionId"], "url": "https://example.com/pricing"}, ) page = call_action("markdown", {"sessionId": opened["result"]["sessionId"]}) print(json.dumps({"country": country, "markdown": page["result"]})) finally: call_action("close", {"sessionId": opened["result"]["sessionId"]})
Production hardening checklist
- Store each country’s result with a timestamp so diffs are reproducible.
- Compare structured fields (price, availability, language) rather than whole-page text where possible.
- Alert on missing variants, not only on changed ones — a market silently losing content is the common failure.
- Keep the country list in config so adding a market doesn’t mean editing code.
Cost and plan notes
Each check is one short session per country over residential egress. Residential traffic costs more per GB than datacenter, so extract markdown rather than downloading full media, and set your real per-page weight in /pricing-calculator.