Puppeteer Screenshot with Indian Date and Number Formats
Use Puppeteer locale and timezone emulation with JavaScript Intl formatters to capture dates and numbers in Indian formats.
Use Puppeteer’s page.emulateLocale('en-IN') to set the browser locale, and use JavaScript’s Intl.NumberFormat and Intl.DateTimeFormat to format values explicitly. If dates should show India-local clock time, also set page.emulateTimezone('Asia/Kolkata') or pass that timezone to the date formatter. Then capture the rendered page with page.screenshot().
Locale and timezone are separate settings: the locale controls regional conventions such as number grouping and date wording; the timezone controls which clock time a timestamp represents. For reliable output, format values in the page itself rather than expecting a screenshot operation to rewrite existing text.
1. Install Puppeteer and capture an India-formatted page
Install Puppeteer in a Node.js project, then save this as screenshot.mjs. The example sets the locale and timezone before creating page content, formats both values explicitly, and writes a PNG.
npm install puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.emulateLocale('en-IN');
await page.emulateTimezone('Asia/Kolkata');
await page.setContent(`
<!doctype html>
<html lang="en-IN">
<meta charset="utf-8">
<title>India-formatted values</title>
<main id="output"></main>
<script>
const amount = 1234567.89;
const date = new Date('2026-10-03T12:00:00Z');
const numberText = new Intl.NumberFormat('en-IN').format(amount);
const dateText = new Intl.DateTimeFormat('en-IN', {
year: 'numeric',
month: 'long',
day: 'numeric',
timeZone: 'Asia/Kolkata'
}).format(date);
document.querySelector('#output').textContent = `${numberText} | ${dateText}`;
</script>
</html>
`);
await page.screenshot({ path: 'screenshot.png' });
} finally {
await browser.close();
}
The exact date wording and punctuation can vary with the browser’s version and locale data. This example does not promise a literal rendered string; inspect the output in the runtime where you deploy it.
2. Choose the locale and timezone deliberately
Locale: regional formatting conventions
en-IN requests English-language conventions for India. For instance, Indian number grouping commonly groups digits in a lakh/crore pattern after the initial three digits. Specify the locale in each formatter when your code controls formatting: this makes the intended behavior visible and avoids relying on a process or browser default. MDN: Intl.NumberFormat.
page.emulateLocale('en-IN') emulates the browser locale for page behavior that reads the browser environment. It does not guarantee that every site will format its content accordingly. A site may use a hard-coded locale, a user profile, server-rendered strings, or its own locale settings. Puppeteer documents locale and timezone emulation as separate page methods in its Page API.
Timezone: which local clock time to display
Choose Asia/Kolkata when a timestamp should appear in India-local time. If the timestamp should remain in another timezone, do not change it just because the number and date conventions should be Indian. You can set the browser timezone with page.emulateTimezone(), or set timeZone in Intl.DateTimeFormat. An explicit formatter timezone controls that formatting operation regardless of ambient defaults. See MDN: Intl.DateTimeFormat.
Set locale and timezone before navigation when the target app reads browser settings during startup. If the page’s own code is under your control, explicitly pass the desired locale and timezone to its formatters as well.
3. Format numbers, currency, and dates explicitly
Use formatter options that match the output you need. Avoid converting formatted values back to numbers: localized strings are for display, not arithmetic.
const amount = 1234567.89;
const grouped = new Intl.NumberFormat('en-IN').format(amount);
const fixedDecimals = new Intl.NumberFormat('en-IN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(amount);
const rupees = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR'
}).format(amount);
const timestamp = new Date('2026-10-03T12:00:00Z');
const indiaDate = new Intl.DateTimeFormat('en-IN', {
year: 'numeric', month: 'long', day: 'numeric', timeZone: 'Asia/Kolkata'
}).format(timestamp);
const indiaDateAndTime = new Intl.DateTimeFormat('en-IN', {
dateStyle: 'medium', timeStyle: 'short', timeZone: 'Asia/Kolkata'
}).format(timestamp);
For dates, select the components or date/time styles your page needs. For numbers, use options such as minimumFractionDigits, maximumFractionDigits, style, and currency as appropriate. Exact punctuation, symbols, digits, and wording depend on locale data and formatter options; verify the rendered result in your target browser runtime.
4. Capture a live website or a specific element
For a live application, set emulation before navigating, wait for the content whose formatting matters, and then capture. Puppeteer’s screenshot guide says to use Page.screenshot() for screenshots. Use an element handle’s screenshot method when only a particular component is needed; the guide notes it attempts to scroll a hidden element into view. Sources: Puppeteer Screenshots guide and Page.screenshot() API.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.emulateLocale('en-IN');
await page.emulateTimezone('Asia/Kolkata');
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.waitForSelector('[data-testid="report-total"]');
await page.screenshot({ path: 'report-full.png', fullPage: true });
const report = await page.$('[data-testid="report-total"]');
if (!report) throw new Error('Report element was not found');
await report.screenshot({ path: 'report-total.png' });
} finally {
await browser.close();
}
Replace the example URL and selector with your page’s URL and a stable selector. If the target page controls its own formatters, browser emulation alone may not change visible content; use its locale settings or format the values in the application before capture.
5. Screenshot options that matter
page.screenshot() accepts options for output and capture scope. Common choices include:
path: where to save the image.type: image format such aspng,jpeg, orwebpwhere supported by the installed Puppeteer/browser version.fullPage: true: capture the full document rather than only the viewport.quality: compression quality for supported lossy formats.clip: capture a specific rectangle.omitBackground: omit the default background where supported and useful for transparency.encoding: choose a buffer or base64 result instead of writing a file, as supported by the API.
Check the installed version’s screenshot API reference for the complete option list and constraints. Full-page images can consume substantial memory for tall documents. For one report card, an element screenshot is often a smaller and more focused artifact.
6. Troubleshooting common formatting and capture issues
| Symptom | Likely cause | Fix |
|---|---|---|
| Numbers still use western digit grouping | The page uses a fixed locale, server-rendered text, or a formatter that ignores browser locale. | Pass 'en-IN' to the page’s Intl.NumberFormat calls or configure the application’s locale. Emulate locale before navigation if the app reads it on startup. |
| Date is formatted as India time but shows an unexpected hour or day | The input timestamp or timezone assumption differs from the intended one. | Confirm whether the input represents UTC, a local time, or another zone. Pass an explicit timeZone to Intl.DateTimeFormat and use a well-defined timestamp. |
| Locale changed but date timezone did not | Locale and timezone are independent settings. | Set page.emulateTimezone('Asia/Kolkata') when browser-local time should be India time, or specify the formatter’s timeZone. |
| The screenshot contains unformatted or stale values | Capture ran before the app rendered or updated the target values. | Wait for a stable selector or application-ready condition before taking the screenshot. Avoid relying on a fixed delay when a meaningful readiness signal is available. |
| Expected punctuation or month wording differs | Formatter options, browser version, or ICU locale data differ. | Assert the behavior your product requires in the deployment runtime; avoid assuming one exact string across all browser builds. |
| Target site ignores locale emulation | The site may use account preferences, server formatting, or a hard-coded locale. | Change the site’s language/region setting where available, or use a page under your control that formats values explicitly. |
| Element screenshot fails or captures the wrong area | The selector matched nothing, matched multiple elements unexpectedly, or the element was not ready. | Wait for the intended selector, check that it exists, and use a stable unique selector. Use full-page capture if the content is not a single element. |
| Browser launch or navigation fails | Runtime dependencies, sandbox configuration, network access, or target-site behavior may prevent the browser from starting or loading. | Review the launch error and deployment environment, ensure the browser can reach the target, and handle navigation failures before attempting capture. |
7. Performance, reliability, and cost
- Wait for useful readiness. Waiting for a target selector or an application-specific ready state avoids capturing incomplete values. Network-idle conditions can be unsuitable for pages with persistent connections or ongoing requests.
- Keep capture scope small. Viewport or element captures generally produce smaller outputs than very tall full-page captures. Use full-page mode only when the complete document is needed.
- Reuse browser processes carefully. For repeated captures, keeping a browser process alive can avoid repeated startup work, while isolating pages and cleaning them up limits cross-request state. Choose concurrency based on available memory and the page sizes you capture.
- Make failures explicit. Put browser closure in a
finallyblock, set operational timeouts in your job runner, and distinguish navigation, rendering, and screenshot failures in logs. - Account for runtime variation. Browser and locale data versions can affect exact formatting. Pin or control the deployment runtime when reproducibility matters, and check representative outputs after upgrades.
- Cost depends on your setup. Puppeteer is a self-managed browser automation approach; budget for the compute and operational work needed to run it. No benchmark or fixed cost is implied here.
8. Or skip the browser setup
If you only need a screenshot, ScreenshotNeo provides a one-request screenshot API and MCP server. The API returns an image or PDF, and the documentation lists request options. It does not set your application’s Indian date or number formats: format those values in your page or application first, then capture the result.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers report the page verdict and billing status.
- An MCP server lets AI agents use screenshot and page information tools.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
9. FAQ
Does en-IN use Indian numerals?
It selects English-language Indian locale conventions. The precise digits and grouping are produced by the runtime’s locale data and formatter options, so check the output in your deployment browser.
Does emulateLocale() change the page’s text?
It emulates the browser locale. It does not translate text or guarantee that a website’s own formatting code will use that locale.
Should I always use Asia/Kolkata?
Only when the displayed timestamp should represent India-local clock time. Locale choice and timezone choice answer different questions.
Can I capture a PDF instead?
Yes. Puppeteer provides page.pdf() for PDF output. Use the PDF API’s paper, margin, and page options when the output is a document rather than an image.


