Geolocation Emulation: Definition and Limits

Geolocation emulation makes the browser's location API report coordinates you choose. See denied vs granted captures and what it does not change: your IP.

By Simon Chiu · Updated

Geolocation emulation is a browser automation setting that overrides the coordinates returned by the JavaScript Geolocation API (navigator.geolocation), so a page behaves as if the device were at a latitude and longitude you choose.

It is how you capture a store locator for Paris, a delivery map for Tokyo or a "near you" widget for New York from one server. It is also easy to overestimate, because it changes one browser API and nothing else. This entry is part of the screenshot and rendering glossary.

A plain explanation

A web page can ask the browser "where am I?" through navigator.geolocation.getCurrentPosition(). On a phone the answer comes from GPS, Wi-Fi or cell towers, after the user grants permission. In an automated browser there is no GPS, and there is nobody to click "Allow". Automation answers both: you set the coordinates, and you grant the permission in code.

Denied and granted, side by side

Two captures of a test page. The top one shows the message Location error 1: User denied Geolocation. The bottom one shows Nearest store to 48.8566, 2.3522 (accuracy 50 m).
A local test page that calls getCurrentPosition, captured with Puppeteer on 2026-10-15. Top: coordinates set but no permission granted. Bottom: the same coordinates with the geolocation permission granted for the page's origin.

Setting the coordinates alone is not enough. Headless Chrome answered the top capture with error code 1, User denied Geolocation, because nobody granted the permission. This is the most common reason emulation "does not work".

What it changes, and what it does not

Signal a site can use Changed by geolocation emulation? What does change it
navigator.geolocation coordinates Yes this setting
IP address (server-side GeoIP, CDN country headers) No a proxy or a machine in that country
Accept-Language, navigator.language No locale
Time zone, Date output No time zone emulation

Most sites that show a different price, currency or cookie notice per country decide on the server, from the IP address. Geolocation emulation does nothing for those. It helps with pages that ask the browser for a position in JavaScript: maps, store finders, weather widgets, "use my location" buttons. For a convincing local capture, set coordinates, locale and time zone together, as in the Playwright listing below.

How it works technically

Chromium exposes the override through the DevTools Protocol command Emulation.setGeolocationOverride, with latitude, longitude and accuracy. The permission is separate: Browser.grantPermissions for an origin. Both apply per browser context, so parallel contexts can sit in different cities. The page receives the values in a normal GeolocationPosition object, with accuracy in meters, and cannot tell them apart from a real fix.

The setting in Puppeteer, Playwright and RenderScreenshot

Puppeteer. Grant the permission on the context, then set the position on the page.

import puppeteer from 'puppeteer';

const url = process.argv[2] ?? 'http://localhost:4630/geo';
const browser = await puppeteer.launch();
try {
  const context = await browser.createBrowserContext();
  // Without this grant the page gets "User denied Geolocation" in headless mode.
  await context.overridePermissions(new URL(url).origin, ['geolocation']);
  const page = await context.newPage();
  await page.setViewport({ width: 640, height: 120 });
  await page.setGeolocation({ latitude: 48.8566, longitude: 2.3522, accuracy: 50 }); // Paris

  await page.goto(url, { waitUntil: 'load' });
  await page.waitForSelector('body[data-geo]'); // the page writes data-geo when the callback runs
  console.log(await page.$eval('#out', (el) => el.textContent));
  await page.screenshot({ path: 'out/geo-puppeteer.png' });
} finally {
  await browser.close();
}

Output: Nearest store to 48.8566, 2.3522 (accuracy 50 m).

Playwright. Everything sits on the context, which makes it easy to set locale and time zone at the same time.

import { chromium } from 'playwright';

const url = process.argv[2] ?? 'http://localhost:4630/geo';
const browser = await chromium.launch();
try {
  const context = await browser.newContext({
    geolocation: { latitude: 35.6762, longitude: 139.6503, accuracy: 50 }, // Tokyo
    permissions: ['geolocation'],
    locale: 'ja-JP',
    timezoneId: 'Asia/Tokyo',
    viewport: { width: 640, height: 120 },
  });
  const page = await context.newPage();
  await page.goto(url);
  await page.locator('body[data-geo]').waitFor({ state: 'attached' });
  console.log(await page.locator('#out').textContent());
  await page.screenshot({ path: 'out/geo-playwright.png' });
} finally {
  await browser.close();
}

Output: Nearest store to 35.6762, 139.6503 (accuracy 50 m).

RenderScreenshot takes browser.geolocation with latitude, longitude and accuracy in a POST body. Combine it with locale and timezone in the same group. See browser options.

curl -X POST https://api.renderscreenshot.com/v1/screenshot \
  -H "Authorization: Bearer $RS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/stores",
    "browser": {
      "geolocation": { "latitude": 51.5074, "longitude": -0.1278, "accuracy": 100 },
      "locale": "en-GB",
      "timezone": "Europe/London"
    },
    "wait": { "until": "networkidle" }
  }' \
  --output stores-london.png
import { Client, TakeOptions } from 'renderscreenshot';

const client = new Client(process.env.RS_API_KEY);
const image = await client.take(
  TakeOptions.url('https://example.com/stores')
    .geolocation(51.5074, -0.1278)
    .locale('en-GB')
    .timezone('Europe/London')
    .waitFor('networkidle')
);

Routing a request through another country, which would change the IP address, is not available in the API today. The localized screenshots guide explains what you can and cannot localize.

Tested on 2026-10-15 with Node 22.18, Puppeteer 24.43.1 (headless shell, Chrome 148) and Playwright 1.64.1 (Chromium 156) against a local test page. The RenderScreenshot snippets follow the API docs and were not run for this page.

Common mistakes

  • No permission grant. The page gets error 1 and shows its "location unavailable" state.
  • Expecting local prices or content. IP-based decisions ignore this setting completely.
  • Capturing before the callback. getCurrentPosition is asynchronous. Wait for the element the page renders with the result, not just for load.
  • Mixed signals. Tokyo coordinates with an en-US locale and a UTC clock can trigger fraud or bot checks. Set the three together.

To capture one page from several cities in a single script, get a free API key.

Frequently asked questions

Does geolocation emulation change the country a website sees?
No. It changes only what the JavaScript Geolocation API returns. Websites that pick a country, currency or language from your IP address, or from a CDN header such as a country code, still see the real location of the machine.
Why does my emulated location give a permission error?
The page must be allowed to use geolocation. In Puppeteer grant the geolocation permission for the page's origin, and in Playwright pass permissions: ['geolocation'] to the context. Without it, headless Chrome denies the request.
What accuracy value should I use?
Accuracy is the radius in meters that the page receives. Use a small value, such as 10 to 100, for store locators that only trust precise fixes. Use a large one to imitate a coarse, network-based location.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.