How to Capture Scheduled Screenshots of a Website in Multiple Languages
Use Playwright to capture localized pages on a schedule, with isolated browser contexts, predictable output files, and reliable checks.
Use a browser automation script to capture each localized page, then have a scheduler or CI runner invoke it at the cadence you need. With Playwright, configure a fresh browser context for each language and timezone, navigate to an explicit localized URL when available, wait for a meaningful page-ready condition, and save a viewport or full-page screenshot with a deterministic filename.
A browser locale can influence language negotiation and formatting, but it cannot make every site select a particular language or market. Verify the rendered language and configure any site-specific URL, cookie, account, or geographic requirements separately. Playwright’s emulation guide documents locale and timezone emulation; its Page API documents navigation and screenshots.
1. Choose what each scheduled capture should prove
Before writing the job, decide which variations and state matter. A useful capture list records the URL, browser locale, browser timezone, viewport, and whether the output should show the viewport or the entire page.
- Use localized URLs where possible. For example, configure a site’s English and German routes directly. A locale preference is not a substitute for a route when the site exposes explicit localized pages.
- Set locale and timezone independently. Locale affects browser language preferences and formatting. Timezone affects the browser’s local time zone. Neither setting alone establishes a visitor’s country or network location.
- Choose viewport or full page. Viewport screenshots are useful for a consistent above-the-fold check. Full-page screenshots capture the scrollable document and are useful when the full layout matters.
- Decide whether runs share state. Fresh contexts isolate cookies and local storage between locale runs. If a site requires login, provide that state deliberately and securely for each context.
- Choose where artifacts and logs go. The script below writes files to a local directory. A CI job can retain or upload them according to that provider’s artifact process.
Keep the scheduler’s timezone separate from the browser timezone in your design. Playwright’s browser timezone emulation does not change the test runner’s timezone. The schedule itself is controlled by your scheduler or CI provider; check its documentation for timezone, daylight-saving, missed-run, retention, and secret-handling behavior.
2. Build a runnable Playwright capture script
This Node.js example uses the Playwright library directly. It creates a new context for each locale, visits an explicit URL, waits for a configured ready selector when provided, and writes a timestamped PNG plus a JSON sidecar containing the capture settings. When no selector is configured, it waits for the page’s load event. Adjust URLs, readiness selectors, viewport, and full-page behavior to match the pages you monitor.
npm init -y
npm install playwright
npx playwright install chromium
Save the following as capture.mjs:
import { chromium } from 'playwright';
import { mkdir, writeFile } from 'node:fs/promises';
const targets = [
{
name: 'home',
locale: 'en-GB',
timezoneId: 'Europe/London',
url: 'https://example.com/en-gb',
readySelector: 'main',
},
{
name: 'home',
locale: 'de-DE',
timezoneId: 'Europe/Berlin',
url: 'https://example.com/de-de',
readySelector: 'main',
},
];
const outputDir = process.env.OUTPUT_DIR ?? 'screenshots';
const fullPage = process.env.FULL_PAGE === 'true';
const viewport = { width: 1440, height: 1000 };
const runAt = new Date();
const runStamp = runAt.toISOString().replace(/[:.]/g, '-');
await mkdir(outputDir, { recursive: true });
const browser = await chromium.launch({ headless: true });
const results = [];
let hadFailure = false;
try {
for (const target of targets) {
const base = `${target.name}-${target.locale}-${runStamp}`;
const imagePath = `${outputDir}/${base}.png`;
const context = await browser.newContext({
locale: target.locale,
timezoneId: target.timezoneId,
viewport,
deviceScaleFactor: 1,
});
try {
const page = await context.newPage();
const response = await page.goto(target.url, {
waitUntil: 'load',
timeout: 45_000,
});
if (response && response.status() >= 400) {
throw new Error(`Navigation returned HTTP ${response.status()}`);
}
if (target.readySelector) {
await page.locator(target.readySelector).waitFor({
state: 'visible',
timeout: 20_000,
});
}
await page.screenshot({ path: imagePath, fullPage });
results.push({
name: target.name,
locale: target.locale,
timezoneId: target.timezoneId,
url: target.url,
imagePath,
capturedAt: new Date().toISOString(),
viewport,
fullPage,
status: 'success',
});
console.log(`Saved ${imagePath}`);
} catch (error) {
hadFailure = true;
results.push({
name: target.name,
locale: target.locale,
timezoneId: target.timezoneId,
url: target.url,
capturedAt: new Date().toISOString(),
status: 'failed',
error: error instanceof Error ? error.message : String(error),
});
console.error(`Capture failed for ${target.locale}:`, error);
} finally {
await context.close();
}
}
} finally {
await browser.close();
await writeFile(
`${outputDir}/run-${runStamp}.json`,
JSON.stringify({ runAt: runAt.toISOString(), results }, null, 2),
);
}
if (hadFailure) process.exitCode = 1;
Run it locally with node capture.mjs. It creates the output directory if needed. Set OUTPUT_DIR to choose another destination, or set FULL_PAGE=true to capture the full scrollable page. The example reports an unsuccessful target and continues with the rest, then exits with a nonzero status so an orchestrator can detect the failed run.
The configured sample domains and routes are placeholders. Replace them with pages you are authorized to access. If you need authenticated content, load credentials or storage state through your secure runtime configuration; do not commit secrets into the script or its output.
3. Configure locale, timezone, and page readiness
Each Playwright browser context acts like an isolated browser profile. Creating one per locale prevents cookies and local storage from a previous language run from silently affecting the next one. If the page requires a common authenticated session, use intentionally provisioned state rather than reusing whichever context ran first. See Playwright’s context isolation guide.
Locale and market behavior
Use locale tags appropriate to the site, such as en-GB or de-DE. A site may use the browser language preference, but may instead select language from its URL, a cookie, account settings, or another signal. Inspect the resulting page or a language selector so that the artifact actually demonstrates the intended language.
Timezone is a separate context option, such as Europe/Paris or Europe/Berlin. Browser timezone emulation changes the timezone visible to page code, not the runner’s system timezone. If your script itself needs a specific timezone, configure the runtime separately, for example with a TZ environment variable on environments that support it. Check the scheduler’s own clock and timezone settings too.
Locale and timezone do not relocate the network request or guarantee regional pricing, inventory, or content. For a true market-specific view, determine whether the target requires a localized route, a locale cookie, account preference, geolocation, or network location. Configure and verify those site-specific mechanisms explicitly.
Wait for the content that matters
Waiting for load is a simple baseline, but modern pages can continue rendering after that event. If a known element indicates readiness, wait for it as shown. Other pages may need a particular heading, a result count, or a loading indicator to disappear. Use a condition that represents usable content rather than a short fixed delay whenever possible.
A fixed delay can be appropriate for a known animation or delayed widget, but it adds runtime and may still be too short under slow conditions. Network-idle waits can also be unsuitable for pages that keep analytics or live connections open. Choose the wait condition according to the site and what the screenshot needs to prove.
4. Make captures comparable over time
Screenshot differences can come from the page or from the capture environment. Playwright notes that rendered output may vary with operating system, browser version, settings, hardware, power source, and headless mode. Keep the runner image and browser version stable, and record relevant configuration with each run. See Playwright’s visual comparison guidance.
- Keep viewport width, height, and device scale factor fixed across runs.
- Keep the browser engine and version consistent if image comparisons matter.
- Use the same locale, timezone, URL, and authentication state for each comparison.
- Wait for fonts and key content to load when their appearance matters.
- Mask or hide known changing regions only when those regions are not the subject of the check.
- Record intentional changes to the runner, browser, fonts, or capture settings in the run metadata.
- Use filenames with page name, locale, and UTC capture time so files do not overwrite one another.
For screenshots intended for human review, stable filenames and a retained run manifest make it easier to identify what changed. For pixel comparison workflows, maintain a reference image and use a consistent comparison environment; do not interpret every pixel difference as a product defect without checking dynamic content and rendering changes.
5. Schedule the script
The browser script defines what to capture. A local job scheduler or hosted CI runner defines when to invoke it and what happens to the files afterward. Add the command node capture.mjs to the scheduler you already operate, then configure its schedule and artifact retention using that provider’s current documentation.
- Pick a cadence. Capture often enough to observe the change rate you care about, while considering page load time and artifact volume.
- Set the scheduler timezone deliberately. Decide whether the schedule follows UTC or a local timezone. Check daylight-saving transitions and the scheduler’s handling of missed or overlapping runs.
- Install the browser runtime. The runner needs Node.js, the script’s dependencies, and the Chromium browser installed by Playwright.
- Provide secrets securely. Add credentials or API keys through the scheduler’s secret mechanism if required; keep them out of source control and logs.
- Retain and inspect artifacts. Save the PNG files and JSON run manifest where reviewers can retrieve them. Set retention to meet your comparison and storage needs.
- Alert on failures. Use the process exit status and run log to surface navigation, readiness, or screenshot failures.
Playwright documents running in CI, but its documentation does not define a particular provider’s schedule syntax, timezone semantics, missed-run behavior, or artifact guarantees. Verify those details with your selected scheduler’s documentation.
6. cURL, Python, and Node.js alternatives with ScreenshotNeo
If you prefer not to install and maintain a browser on your scheduled runner, ScreenshotNeo is a website screenshot API and MCP server. Your scheduler can call its screenshot endpoint for each localized URL. Pass the locale and timezone for the page you want, and check that the target site actually serves that language: browser settings do not override site-specific routing rules.
For setup and the available request options, see the ScreenshotNeo API documentation. Use an API key in place of YOUR_API_KEY.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/de-de \
-d locale=de-DE \
-d timezone=Europe/Berlin \
-o de-DE-homepage.webp
Python
import requests
params = {
"access_key": "YOUR_API_KEY",
"url": "https://example.com/de-de",
"locale": "de-DE",
"timezone": "Europe/Berlin",
}
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params=params,
timeout=90,
)
r.raise_for_status()
with open("de-DE-homepage.webp", "wb") as image:
image.write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/de-de',
locale: 'de-DE',
timezone: 'Europe/Berlin',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('de-DE-homepage.webp', image)
);
Repeat the call for each URL and locale in your schedule. ScreenshotNeo supports many capture settings, including full-page screenshots, device and viewport settings, wait conditions, custom headers and cookies, caching with a chosen TTL, and async jobs with signed webhooks. Review the docs for exact parameter names and formats before using options beyond the examples above.
Or skip the browser setup
With ScreenshotNeo, one GET request takes a screenshot. Cookie banners, popups, and chat widgets are removed before the shot, and each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing status. Its MCP server lets AI agents use screenshot tools.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/de-de \
-o de-DE-homepage.webp
The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots. Try ScreenshotNeo by creating a free account.
7. Troubleshooting scheduled captures
| Symptom | Likely cause | What to change |
|---|---|---|
| The page appears in the wrong language | The site does not use the browser locale for language selection, or a URL, cookie, or account preference takes precedence. | Use the explicit localized URL when available. Inspect the rendered language and configure the site’s required preference deliberately. |
| The time or date on the page is unexpected | Browser timezone, runner timezone, and scheduler timezone are separate. | Set the context timezone and the scheduler timezone intentionally. Set the runner timezone separately if script logic depends on it. |
| A run captures a loading screen or incomplete page | The page’s meaningful content loads after the load event. | Wait for a stable page-specific selector or readiness condition. Increase the relevant timeout only after choosing the right condition. |
| Navigation times out intermittently | Slow target response, network variation, or a wait condition that never completes. | Check the target and runner network access. Use an appropriate readiness condition and a reasonable timeout; log the URL and failure for the affected locale. |
| The wrong locale seems to inherit consent or another state | Contexts or persistent profiles are being reused. | Create a new context for each locale run, or explicitly provision the required storage state for each one. |
| Images are missing in full-page captures | Lazy-loaded content may not have been requested before the capture, or remote assets failed. | Wait for the relevant image or content to appear and verify remote assets are reachable. For pages that lazy-load on scroll, add a deliberate scroll-and-wait step suited to the page. |
| Images differ even though the site did not change | Browser version, host rendering environment, viewport, fonts, dynamic content, or animation timing changed. | Stabilize the runner and browser settings, record the capture environment, and mask only irrelevant dynamic regions. |
| The scheduler reports success despite a missing image | The script may not propagate capture failures or may not verify its outputs. | Preserve the nonzero exit status on any failed target, inspect the JSON run manifest, and configure the scheduler to alert on failed runs. |
| Files overwrite each other | The output path omits locale, page name, or capture time. | Use deterministic names that include those fields, as in the example. |
| The scheduled job has no artifacts to inspect | Output exists only on an ephemeral runner or its retention setting is too short. | Configure artifact upload or durable storage and verify the selected provider’s retention behavior. |
8. Performance, reliability, and cost
A scheduled run’s duration depends on the number of pages and locales, target response time, readiness waits, image loading, and whether captures run sequentially or in parallel. The example runs sequentially to keep resource use predictable and isolate failures. If you parallelize, cap concurrency so that the browser runner and target sites are not overloaded, and make filenames unique across simultaneous runs.
Full-page images can take longer and use more memory than viewport captures, particularly for long documents. Capture the smallest area that answers the monitoring question. A fixed viewport improves comparability and avoids accidental differences caused by responsive reflow.
For reliability, capture per locale in isolation, record failures instead of silently skipping them, set timeouts on navigation and readiness checks, and make the overall job exit nonzero if any capture fails. Scheduler reliability, retries, overlap prevention, timezone rules, and artifact retention are provider-specific and should be checked against the scheduler you choose.
For a DIY setup, budget for the machine or CI minutes, browser installation and updates, artifact storage, and maintenance of the script. The research sources do not establish prices for scheduler vendors, so compare their current documentation and pricing for your workload.
FAQ
Does setting locale translate a website?
No. It emulates a browser locale. The site decides how to use that signal. Use a localized URL or the site’s documented language preference and verify the rendered page.
Can one browser context capture every language?
It can, but separate contexts reduce accidental carryover of cookies and local storage. Reuse state only when that is intentional for the site being captured.
Does the browser timezone change when the scheduled job runs?
Only if you configure it for the browser context. The scheduler clock, runner timezone, and browser timezone are distinct settings.
Should I capture the viewport or the full page?
Use viewport capture for a consistent visible region and full-page capture when the entire scrollable document is needed. Keep the choice consistent for comparisons.
How can I tell whether a scheduled capture is trustworthy?
Retain the image with metadata for URL, locale, timezone, viewport, browser version, timestamp, and result; then inspect failed or unexpected outputs instead of treating every file as valid.


