ScreenshotNeo

BlogHow-to

How to Capture Product Page Screenshots with a Specific Browser Locale and Currency

Set a Playwright browser locale before navigation, choose the store’s currency through its own controls, and verify the product details before capture.

By the ScreenshotNeo team4 October 202610 min read

Short answer: Set the browser locale on a fresh Playwright browser context before navigating to the product page. Then use the retailer’s own region or currency controls to select the currency, wait for the product name and price to appear, and verify the displayed currency before saving the screenshot. A locale such as de-DE changes browser language signaling and locale-sensitive formatting; it does not universally force a merchant to display euros.

This guide uses Playwright with Node.js. The retailer URL and selectors in examples are placeholders: replace them with the real page and controls for the site you are authorized to capture. Playwright’s locale and timezone documentation describes locale emulation, and its screenshot options cover viewport and full-page output.

1. What browser locale controls—and what it does not

Playwright’s context locale affects browser language information such as navigator.language, the Accept-Language request header, and number and date formatting rules. Set it before the first navigation so the initial page request uses the intended locale.

Currency selection is a separate, retailer-specific decision. A store may use a region picker, a currency menu, a saved preference, account settings, or other site behavior. Do not assume that changing locale changes the store’s currency. Select the currency through the site’s supported interface and verify the resulting price and currency indicator. A browser context provides cookie APIs, but that alone does not mean any particular retailer stores its currency choice in a cookie.

Setting or action What it is for What to verify
Context locale Browser language signaling and locale-sensitive formatting Language and formatting on the page
Store region or currency control The merchant’s own market and currency behavior Currency code or symbol and the displayed price
Context timezone Timezone-dependent page behavior Only relevant when displayed content depends on time or timezone
Viewport Responsive layout and visible content Dimensions match the intended screenshot

2. Set up Playwright

Use a supported Node.js installation, create a project, and install Playwright. Install the Chromium browser binary for Playwright if it is not already available in the environment.

mkdir product-page-capture
cd product-page-capture
npm init -y
npm install playwright
npx playwright install chromium

Save the following as capture-product.mjs. It creates a new context with locale and viewport settings, navigates, waits for product-specific elements, checks the visible price text against an expected currency marker, and saves a screenshot. Since selectors and currency display vary by store, adapt the marked values to the actual page. The script deliberately fails if the expected price marker is absent instead of silently creating a misleading image.

import { chromium } from 'playwright';

const productUrl = process.env.PRODUCT_URL ?? 'https://shop.example/product';
const locale = process.env.LOCALE ?? 'de-DE';
const expectedCurrency = process.env.CURRENCY_MARKER ?? '€';
const outputPath = process.env.OUTPUT ?? 'product-de-DE.png';

// Replace these selectors with stable selectors from the target store.
const productNameSelector = '[data-testid="product-name"]';
const priceSelector = '[data-testid="product-price"]';

const browser = await chromium.launch({ headless: true });
try {
  const context = await browser.newContext({
    locale,
    viewport: { width: 1440, height: 1000 },
  });
  try {
    const page = await context.newPage();
    await page.goto(productUrl, { waitUntil: 'domcontentloaded' });

    // If required, select region/currency using the retailer's supported UI here.
    // For example, open its market menu, choose the intended market, and submit.
    // Do not assume changing `locale` performs this step.

    const productName = page.locator(productNameSelector);
    const price = page.locator(priceSelector);
    await productName.waitFor({ state: 'visible', timeout: 15000 });
    await price.waitFor({ state: 'visible', timeout: 15000 });

    const nameText = (await productName.innerText()).trim();
    const priceText = (await price.innerText()).trim();
    if (!nameText) throw new Error('Product name is empty');
    if (!priceText.includes(expectedCurrency)) {
      throw new Error(`Expected currency marker ${expectedCurrency}; saw: ${priceText}`);
    }

    await page.screenshot({ path: outputPath, fullPage: false });
    console.log(`Saved ${outputPath}: ${nameText} — ${priceText} (${locale})`);
    await context.close();
  } finally {
    // Context cleanup also happens if a wait or validation fails.
    if (context.pages().length) await context.close();
  }
} finally {
  await browser.close();
}

Run it with environment variables so the target and expected currency are explicit:

PRODUCT_URL='https://your-store.example/product/sku' \
LOCALE='de-DE' \
CURRENCY_MARKER='€' \
OUTPUT='product-germany.png' \
node capture-product.mjs

The example checks that the expected marker occurs in the price text, but this is only a basic guard. For stronger validation, use the page’s currency code or market label as a separate locator and assert the exact product identity, price text, and selected region. Symbols can be ambiguous: for example, a symbol by itself may be used by more than one currency.

3. Choose the right capture and wait strategy

Wait for meaningful page state

Retail pages can hydrate product data after the initial HTML arrives. Waiting for domcontentloaded alone does not prove that the product price is ready. Wait for the actual product name, price, selected currency, and any image that must appear in the capture. Prefer a locator tied to the site’s content over an arbitrary fixed sleep.

If the store changes price after a region selection, wait for the price or region indicator to update before capturing. If the page uses a loading state, wait for that state to disappear as well. Avoid relying on networkidle as the only signal on pages that keep analytics, chat, or other background requests open; a specific content condition is usually more meaningful.

Viewport or full page

By default, page.screenshot() captures the visible viewport. Set fullPage: true to capture the full scrollable page. A viewport image is usually clearer for the product title, selected option, price, and purchase controls. Full-page output is useful when the page’s specifications or disclosures matter too. Long pages can create large image files and may require additional time and memory.

// Viewport capture
await page.screenshot({ path: 'product.png' });

// Full scrollable page
await page.screenshot({ path: 'product-full.png', fullPage: true });

// JPEG output with an explicit quality
await page.screenshot({ path: 'product.jpg', type: 'jpeg', quality: 85 });

Locale, timezone, and viewport configuration

Set the locale when creating the browser context. Set timezone separately only when the page content depends on it. The timezone setting affects the browser page; it does not change the test runner’s own timezone. Set viewport dimensions explicitly when consistent responsive layout matters.

const context = await browser.newContext({
  locale: 'en-GB',
  timezoneId: 'Europe/London',
  viewport: { width: 1365, height: 900 },
});

Use a documented language-region locale such as en-GB or de-DE. A region in the locale can influence formatting and language negotiation, but it is not a promise about what market or price the retailer serves.

Cookies and retained state

A new context starts with isolated browser state, which helps make runs repeatable. If the store’s supported currency selection persists across navigation in the same context, perform that selection before capture. When reproducing an intentional state, use the retailer’s supported interface or documented test setup. Do not guess a cookie name or value: storage behavior is site-specific. Avoid reusing authenticated or customer-specific state for images that will be shared.

4. cURL, Python, and Node.js alternatives

cURL and Python’s standard HTTP clients do not run a browser, so they cannot render a product page or take a browser screenshot on their own. They are useful for checking a response or downloading an already-created image, but locale emulation, JavaScript rendering, interacting with a currency selector, and screenshot capture require browser automation or a screenshot service.

cURL: inspect the page response

This fetches HTML only. The header can express a language preference, but it does not emulate a browser or guarantee the retailer’s currency.

curl -L \
  -H 'Accept-Language: de-DE,de;q=0.9,en;q=0.8' \
  'https://shop.example/product' \
  -o product.html

Python: request HTML, not a rendered screenshot

import requests

url = 'https://shop.example/product'
response = requests.get(
    url,
    headers={'Accept-Language': 'de-DE,de;q=0.9,en;q=0.8'},
    timeout=30,
)
response.raise_for_status()
with open('product.html', 'wb') as output:
    output.write(response.content)
print(response.status_code, response.url)

For an actual screenshot from Python, use a browser automation library that launches a browser and supports context locale settings. A plain HTTP response does not execute the page’s JavaScript or operate its currency menu.

Node.js: request HTML, not a rendered screenshot

const response = await fetch('https://shop.example/product', {
  headers: { 'Accept-Language': 'de-DE,de;q=0.9,en;q=0.8' },
  redirect: 'follow',
  signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status} ${response.statusText}`);
}
const html = await response.text();
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('product.html', html)
);
console.log(response.url);

Use the Playwright Node.js workflow above when the deliverable must be a screenshot of the rendered page.

5. Or skip the browser setup

For a one-request capture, use ScreenshotNeo, a website screenshot API and MCP server. Its API takes a URL and can return a PNG, JPEG, WebP, or PDF. Use the API documentation for request options and account setup.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://shop.example/product -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://shop.example/product"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://shop.example/product' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

A screenshot API call does not by itself establish a retailer’s regional currency selection. Confirm that the returned page displays the intended currency; if the store requires an interactive region choice, configure the page’s supported state first. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

6. Troubleshooting

Symptom Likely cause Fix
The page is in the expected language but shows the wrong currency Locale negotiation and merchant currency selection are separate Use the store’s region or currency control, wait for it to apply, then verify the currency code or market label
The price is missing or stale Product data has not finished hydrating, or the region change is still processing Wait for the product-specific price locator and an updated currency indicator; avoid a fixed delay as the only condition
The screenshot shows a consent dialog over the product A consent prompt is blocking the page Use the site’s supported consent control in the automation flow, or use a service configured to remove consent overlays before capture
Selector wait times out Placeholder selector does not match the page, product is unavailable, or content is inside a frame Inspect the page structure, choose a stable selector, check availability, and locate content in the correct frame
Currency assertion fails despite a correct price The site may show a code, localized symbol, or symbol placement different from the expected text Assert against the actual displayed currency label and price format; do not rely on one universal symbol format
The screenshot uses the wrong responsive layout No explicit viewport was set or a mobile preset differs from the target Set width and height on the context before navigation and keep them fixed across captures
Screenshot is clipped or excessively tall Viewport capture was used when a full-page image was intended, or the full page has unusually long content Choose fullPage: true only when needed; capture the product section or viewport for a concise image
Navigation fails or returns an interstitial Network issue, access restriction, bot check, or target page behavior Check the URL and response, retry transient network failures with a limit, and use an authorized test environment when access controls prevent capture

7. Performance, reliability, and cost

  • Wait on the right condition: product and price locators make capture timing more deterministic than a guessed sleep. Use a finite timeout so failures surface clearly.
  • Reuse a browser process carefully: for many captures, launching one browser and creating a fresh context per independent locale or market reduces repeated startup work while keeping state isolated. Close pages, contexts, and the browser in cleanup paths.
  • Keep output proportional: viewport screenshots are quicker and smaller than very tall full-page captures. Choose PNG for lossless output, or JPEG where a smaller photographic image is acceptable. Match the capture dimensions to the intended use.
  • Validate before publishing: check product identity, price, currency, selected options, and overlays in the saved image. A successful screenshot call only proves an image was produced, not that it represents the intended market.
  • Control variation: set locale, viewport, currency selection, and any relevant timezone explicitly. Retailer inventory, promotions, and prices can change independently, so record capture time when the image is evidence or part of a comparison.
  • Budget for failures: local automation consumes runtime and browser resources even when navigation or validation fails. Retry only transient failures, use bounded retries, and avoid repeated runs against a retailer that denies access. ScreenshotNeo states that bot checks, blank pages, failed loads, and cache hits cost nothing; consult its pricing and docs for current plan details.

8. Frequently asked questions

How do I take a screenshot of a product page in another currency?

Set the browser locale before navigation, choose the currency using the retailer’s own control if needed, then verify the displayed currency and price before capturing.

Does de-DE force a website to show euros?

No. It configures browser locale behavior such as language signaling and number and date formatting. The retailer decides how to select or infer its market and currency.

Should I use a fixed wait such as five seconds?

Usually not as the only readiness check. Wait for the product-specific name and price, and for the region or currency selection to finish.

Can cURL or Python requests make the screenshot?

Not by themselves. They fetch HTTP responses; they do not render a browser page, run its JavaScript, or capture the rendered result.

Should I use viewport or full-page capture?

Use the viewport for the purchase summary and full-page capture when below-the-fold details are required. Check the resulting image for clipping and readability.