ScreenshotNeo

BlogHow-to

How to Screenshot Google SERPs in Incognito Mode with Playwright

Use a fresh Playwright browser context to capture a Google results page without reusing a browser profile. Includes runnable code, options, and troubleshooting.

By the ScreenshotNeo team4 October 20267 min read

To screenshot a Google results page in an incognito-like session, launch Playwright, create a fresh non-persistent browser context with browser.newContext(), open a page, navigate to the search URL, save a screenshot, then close the context. The context does not reuse an existing browser profile or write its browsing data to disk. It provides session isolation; it does not make a search anonymous or guarantee identical results across captures.

  1. Install Playwright and its Chromium browser.
  2. Set the viewport when the rendered layout matters.
  3. Create a new context for this capture.
  4. Navigate to Google Search and save the screenshot.
  5. Close the context and browser, including when navigation fails.

Runnable Playwright example

This JavaScript example saves a viewport screenshot as google-serp.png. Replace the query with your own search terms.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext({
    viewport: { width: 1365, height: 900 },
  });
  try {
    const page = await context.newPage();
    const query = new URLSearchParams({ q: 'playwright' });
    await page.goto(`https://www.google.com/search?${query}`, {
      waitUntil: 'domcontentloaded',
      timeout: 30000,
    });
    await page.screenshot({ path: 'google-serp.png' });
  } finally {
    await context.close();
    await browser.close();
  }
})();

Save it as capture-serp.js and run node capture-serp.js. Install the package and browser first:

npm install playwright
npx playwright install chromium

The workflow uses Playwright’s documented non-persistent browser context, page screenshot, and navigation APIs. The example is an instructional synthesis of these APIs, not a claim of execution or testing.

What “incognito” means in Playwright

A new browser.newContext() creates an isolated, non-persistent session. Playwright’s context example describes this as an incognito browser context. Cookies and other session state belong to that context rather than an existing browser profile, and the context is not persisted to disk. Closing the context discards its session.

This is useful for clean captures and repeatable setup, but it does not promise anonymity, location-neutral results, or the same SERP for different users or runs. Google results can vary with time and context. Do not infer ranking changes from one screenshot alone.

Choose the capture dimensions and scope

Viewport

Set viewport in browser.newContext() before navigation. Sites respond to viewport size, so it can change the layout as well as the output image dimensions. For a mobile capture, use the intended phone-sized viewport before loading the page. Record dimensions when comparing captures.

const context = await browser.newContext({
  viewport: { width: 390, height: 844 },
});

Viewport dimensions describe the browser page area; they are not a guarantee that every device-specific behavior is reproduced. If your comparison depends on browser engine, device emulation, locale, or other context settings, record those choices too.

Viewport screenshot or full page

page.screenshot({ path: 'google-serp.png' }) captures the visible page area by default. To include the full document, use fullPage: true:

await page.screenshot({ path: 'google-serp-full.png', fullPage: true });

A full-page capture can be much taller and may include content below the fold. Use it when you need the whole results document; use the default for a view of what fits on screen. Google pages are dynamic, so the two captures may include different content or page positions.

Image format and output

Playwright infers the screenshot format from the filename extension when saving to a path. Use .png for PNG or .jpeg for JPEG. For a buffer instead of a file, omit path and keep the returned bytes:

const imageBytes = await page.screenshot({ type: 'jpeg', quality: 85 });

JPEG quality applies to JPEG screenshots. Choose format and quality based on whether you need sharp text, smaller files, or lossless output. See the official screenshot options for supported options.

Make captures easier to compare

  1. Use a fresh context for each independent capture unless you deliberately need to reuse state.
  2. Keep the query, viewport, browser engine and version, and capture time with the image.
  3. Specify locale or region-related context only when it is part of the comparison, and record the setting. It does not guarantee identical results.
  4. Use the same screenshot scope and image format for every capture.
  5. Allow the page to reach the state you intend to document. The example waits for DOM content, not every image or asynchronous element.

For a page that needs more time to settle, you can wait for a selector that is relevant to the page, or choose a navigation wait condition appropriate to your workflow. Waiting for network idle can be unreliable on pages with ongoing requests; a fixed delay also does not prove that content is complete. Google may alter or defer elements after initial navigation.

Playwright supports screenshot styling options, but if the image is presented as a Google Search screenshot, do not use them to alter the interface, suggestions, or results. Keep the capture faithful to the page shown.

Complete Python and cURL alternatives

Playwright is available in Python as well. Install the Python package and Chromium:

pip install playwright
playwright install chromium
import asyncio
from urllib.parse import urlencode
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        context = await browser.new_context(
            viewport={"width": 1365, "height": 900}
        )
        try:
            page = await context.new_page()
            url = "https://www.google.com/search?" + urlencode({"q": "playwright"})
            await page.goto(url, wait_until="domcontentloaded", timeout=30000)
            await page.screenshot(path="google-serp.png")
        finally:
            await context.close()
            await browser.close()

asyncio.run(main())

For a quick HTTP response check, cURL can fetch the search page HTML, but it does not run a browser or render a screenshot. It is not a substitute for Playwright when the goal is a visual SERP capture.

curl -L --max-time 30 \
  'https://www.google.com/search?q=playwright' \
  -o google-search.html

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; its clean-shot flow accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. AI agents can use its MCP tools, including take_screenshot. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is on every plan. See the ScreenshotNeo site and API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://www.google.com/search?q=playwright \
  -o google-serp.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://www.google.com/search?q=playwright",
    },
    timeout=90,
)
open("google-serp.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://www.google.com/search?q=playwright',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('google-serp.webp', bytes));

These calls capture the requested URL; they do not provide Playwright’s isolated browser context or promise that Google will return the same SERP. Sign up for 1,000 free screenshots a month with no card.

Troubleshooting

Symptom Likely cause What to do
Cannot find module 'playwright' The package is not installed in this project or Node environment. Run npm install playwright in the project and run the script with that environment’s Node.
Browser executable missing Playwright’s Chromium browser has not been installed. Run npx playwright install chromium.
Navigation times out The page did not reach the chosen wait condition before the timeout. Check network access and the URL. Increase the timeout if appropriate, or use domcontentloaded when you do not need every resource to finish.
Screenshot is blank or incomplete Capture happened before the relevant content appeared, or the page failed to load. Inspect the final page URL and content, wait for a meaningful page state, and handle navigation errors. A screenshot call does not establish that the page loaded successfully.
Unexpected mobile or desktop layout The viewport was omitted or configured after navigation. Set viewport dimensions when creating the context, before opening the page.
Results differ between runs SERPs are dynamic and may differ by time and context. Record query, capture time, viewport, engine/version, and explicit context settings. Do not treat context isolation as a reproducibility guarantee.
Context or browser remains open after an error Cleanup was skipped on an exceptional path. Put context.close() and browser.close() in a finally block as in the examples.

Reliability, performance, and cost

A screenshot workflow consumes browser and page-loading resources, so reuse a launched browser for a batch if you need multiple captures, while creating a separate context for each independent session. Always close pages or contexts when finished. A fresh browser process per URL has extra startup work; context reuse avoids repeatedly starting Chromium while retaining session separation.

There is no meaningful fixed capture-time promise here: network conditions, page resources, Google response behavior, and chosen wait conditions affect completion. Set a timeout, catch navigation failures, and save metadata alongside images when comparisons matter. Playwright itself is software you run, so account for the compute and maintenance of the browser environment you choose. No benchmark or cost estimate is implied.

Google screenshot presentation and permissions

Google’s Search screenshot guidance says to show Search in positive or neutral, realistic ways and not alter the interface, suggestions, or results. It says permission is not required for Search screenshots in print educational or instructional materials, but that guidance is not a blanket license for websites, promotions, or third-party material shown in results. Third-party content may require approval from its owner, and Google says Search use in advertisements requires its approval. Google and the Google logo are trademarks of Google LLC.

FAQ

Does a new Playwright context erase my regular browser cookies?

No. It creates a separate session rather than opening or modifying your usual browser profile.

Can this guarantee the same search results every time?

No. A fresh context controls session reuse, but it does not make Google’s dynamic results identical across time or users.

Should I use a persistent context for incognito?

No. Use browser.newContext() for an isolated, non-persistent session. Persistent contexts are intended for a user-data directory and retained browser state.

Can I capture only the visible viewport?

Yes. The default screenshot captures the viewport; set fullPage: true when you need the full document.