ScreenshotNeo

BlogHow-to

How to Take a Puppeteer Screenshot of a GST Invoice Web Page

Capture a GST invoice page with Puppeteer, wait for its real content to load, and save a full-page image or print-ready PDF.

By the ScreenshotNeo team4 October 20267 min read

Use Puppeteer’s page.screenshot() after the invoice has rendered. Set the viewport before navigating, wait for the invoice’s actual ready state, then choose a viewport-only or full-page capture. The example below saves a full-page PNG; replace the URL and readiness selector with values from your invoice application.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 900 });
  await page.goto('https://example.com/invoice/123', {
    waitUntil: 'networkidle2',
  });
  // Replace this with a selector that exists on your invoice page.
  await page.waitForSelector('[data-invoice-ready]', { timeout: 15000 });
  await page.screenshot({ path: 'gst-invoice.png', fullPage: true });
} finally {
  await browser.close();
}

Install Puppeteer in a Node.js project with npm install puppeteer. Puppeteer’s Page.screenshot() API captures the rendered page and supports saving to a path. The readiness selector above is illustrative: inspect your application and use its actual invoice container or loaded-state marker.

1. Capture a GST invoice page with Puppeteer

A reliable capture has four steps: launch a browser, set the viewport, navigate to the invoice, and wait for the specific invoice content before saving. For a simple static page, navigation completion may be enough. For an application that fetches invoice data asynchronously, wait for an application-specific signal as in the example.

networkidle2 is a useful navigation condition, but it does not prove the invoice has finished rendering. Some pages continue polling or loading analytics; others render invoice data after the network becomes quiet. Treat it as a navigation condition, then wait for the state that means the invoice is ready.

Use the correct page and access context

Use the invoice’s real URL and the same access method a legitimate user or your application uses. If the invoice requires authentication, provide the appropriate session or credentials through your application’s established process. Do not place secrets directly in source code or capture data you are not authorized to access. If the page redirects, check the final URL and confirm the intended invoice was rendered before taking the screenshot.

2. Choose viewport or full-page capture

With fullPage: true, Puppeteer captures the page beyond the visible viewport, which is useful when invoice line items or totals extend below the fold. Omit it or set it to false to capture only the current viewport. Very tall pages may produce large images; if the invoice is intended as a printable document, PDF output is often a better fit.

Need Setting What to expect
Entire invoice page fullPage: true Captures content beyond the viewport.
Visible portion only fullPage: false or omit it Captures the current viewport dimensions.
Retina-sized output deviceScaleFactor on the viewport Raises output pixel density and file size.
Consistent responsive layout Set width and height before navigation Lets the page lay out for the intended viewport.

For example, choose a larger device scale factor when the image will be zoomed or printed, while remembering that it increases memory use and output size:

await page.setViewport({
  width: 1280,
  height: 900,
  deviceScaleFactor: 2,
});

Viewport dimensions affect responsive layouts, so set them before page.goto(). Puppeteer’s Page API documents page operations and device emulation, which changes viewport metrics and user-agent behavior.

3. Wait for the invoice content to be ready

Choose a readiness check that matches the application. A selector can indicate that the invoice container exists, but existence alone may not mean its values have populated. If the app exposes a loaded marker, wait for that marker; if it does not, wait for a stable element and verify the expected content before capture.

// Wait for an invoice container to exist.
await page.waitForSelector('.invoice', { timeout: 15000 });

// Or wait until the application marks the invoice as fully loaded.
await page.waitForFunction(() => {
  const invoice = document.querySelector('.invoice');
  return invoice?.getAttribute('data-state') === 'loaded';
}, { timeout: 15000 });

The selectors and state names in this snippet are examples, not Puppeteer defaults. Replace them with the actual DOM and application state. If images, fonts, or client-side calculations affect the final appearance, include those in your readiness criteria. Avoid relying on a fixed sleep as the only readiness check: it can waste time on fast runs and still be too short on slow ones.

4. Save a screenshot or a PDF

Use page.screenshot() for an image such as PNG, JPEG, or WebP. Set the filename extension and format option consistently when choosing a format. Use page.pdf() when the goal is document delivery or printing; Puppeteer applies print CSS by default, so the result may differ from the on-screen invoice.

// PNG image, full page
await page.screenshot({ path: 'gst-invoice.png', fullPage: true });

// JPEG image with explicit quality
await page.screenshot({
  path: 'gst-invoice.jpg',
  type: 'jpeg',
  quality: 90,
  fullPage: true,
});

// PDF with print styling (the default media type)
await page.pdf({ path: 'gst-invoice.pdf', format: 'A4', printBackground: true });

For a PDF that should reflect screen media instead of print styling, call await page.emulateMediaType('screen') before page.pdf(). PDF rendering may adjust colors for printing. CSS using -webkit-print-color-adjust: exact can request exact color rendering where appropriate, but inspect the generated document because the page’s print styles and browser rendering determine the result. See Puppeteer’s PDF API and emulateMediaType API.

5. Check the invoice content separately from the capture

A screenshot records what the browser rendered. It does not determine whether an invoice complies with GST rules. The CBIC invoice rules list particulars such as supplier name, address and GSTIN; a consecutive serial number unique for the financial year; issue date; recipient information where applicable; HSN code or accounting code for services; description and values; and tax particulars. Rule 46 on the government tax information portal also states that the serial number must not exceed sixteen characters and must be unique for a financial year. Applicability can depend on the transaction and taxpayer, so consult current rules and amendments for compliance decisions. See the CBIC rules source and Rule 46 on the government tax information portal.

As a capture check, confirm that the saved file opens, the expected invoice and totals appear, and no loading state, error message, clipped content, or unexpected print layout was captured.

6. Troubleshooting Puppeteer invoice screenshots

Symptom Likely cause Fix
Invoice is blank or missing values Capture ran before client-side data rendered. Wait for the application’s loaded state or verify expected invoice content before capture.
Navigation or selector timeout Slow page, wrong URL, redirect, incorrect selector, or a readiness condition that never occurs. Check the final URL and selector in the rendered DOM; adjust the timeout only after confirming the condition is correct.
Capture hangs with network idle Long polling, streaming, analytics, or persistent requests prevent the chosen idle condition. Use a less restrictive navigation condition and wait for the invoice’s own ready marker.
Invoice is cut off Viewport-only capture or content rendered after capture. Use fullPage: true and wait for content and images that affect the page height.
Layout differs from the expected page Viewport or device emulation was set too late or does not match the intended display. Set viewport dimensions before navigation and capture using the intended scale.
PDF colors or page breaks differ PDF uses print CSS and print rendering behavior by default. Review print styles, choose screen media if needed, and inspect the resulting PDF.
Browser process fails to launch Browser installation or runtime dependencies are unavailable in the environment. Check Puppeteer’s installation requirements and deployment environment, and ensure the browser process can launch there.

7. Performance, reliability, and cost

Browser automation starts and runs a browser process, so reuse a browser for multiple captures in a controlled worker instead of launching one for every invoice when throughput matters. Always close pages and browsers when finished, including on failures. Set explicit navigation and readiness timeouts so a stuck page does not occupy a worker indefinitely.

Keep the viewport and output scale only as large as the consumer needs. Full-page and high-density captures can use more memory and produce larger files. For repeatable results, fix the viewport, wait on page-specific readiness, and keep the browser version and rendering environment consistent. Puppeteer itself does not assign a per-screenshot service fee; operational costs come from the compute, storage, and processing environment in which you run it.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns an image or PDF, without managing a browser installation for this capture. See the ScreenshotNeo documentation for request options.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/invoice/123"},
    timeout=90,
)
open("gst-invoice.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/invoice/123',
});
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('gst-invoice.webp', res);

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 use tools to take screenshots, get page information, and capture PDFs. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

Frequently asked questions

Does a screenshot prove a GST invoice is valid?

No. It shows the rendered page only. Check the applicable invoice rules and the invoice data separately.

Should I save the invoice as an image or PDF?

Choose an image for a visual snapshot or image processing. Choose PDF for printing or document delivery, and account for print CSS.

Why use an application-specific wait instead of a delay?

A page-specific condition ties capture to invoice readiness. A fixed delay has no knowledge of whether the invoice loaded.