ScreenshotNeo

BlogHow-to

How to Take a Screenshot of an Authenticated Page with Playwright in C#

Log in with Playwright for .NET, wait for the protected page, and capture it. Learn how to reuse browser state and troubleshoot expired or missing authentication.

By the ScreenshotNeo team4 October 20268 min read

To screenshot an authenticated page with Playwright for .NET, finish the site’s login flow, wait until the protected page is actually ready, then call await page.ScreenshotAsync(new() { Path = "authenticated.png" });. For later runs, save the browser context’s storage state after login and load it into a new context. Treat that state file like a password: it can contain credentials that allow someone to impersonate the account.

1. Install Playwright for .NET

Start with a .NET project, add the Playwright package, and install the browser binaries. The commands below use the .NET CLI and Chromium; choose the browser your application needs.

dotnet new console -n AuthenticatedScreenshot
cd AuthenticatedScreenshot
dotnet add package Microsoft.Playwright
dotnet build
pwsh bin/Debug/net8.0/playwright.ps1 install chromium

Use the generated script path for your target framework and build configuration if it differs. Playwright’s .NET getting-started guide covers setup and launch.

2. Log in and capture the protected page

Replace the example URLs, selectors, and readiness condition with the target site’s actual login flow. Keep credentials out of source code; read them from environment variables or a secret store. This sample waits for a signed-in marker before opening the report, then saves a viewport screenshot.

using Microsoft.Playwright;

var username = Environment.GetEnvironmentVariable("APP_USERNAME")
    ?? throw new InvalidOperationException("Set APP_USERNAME.");
var password = Environment.GetEnvironmentVariable("APP_PASSWORD")
    ?? throw new InvalidOperationException("Set APP_PASSWORD.");

using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
await using var context = await browser.NewContextAsync();
var page = await context.NewPageAsync();

await page.GotoAsync("https://example.com/login");
await page.GetByLabel("Email").FillAsync(username);
await page.GetByLabel("Password").FillAsync(password);
await page.GetByRole(AriaRole.Button, new() { Name = "Sign in" }).ClickAsync();

// Prefer a condition that proves the login finished, such as a stable
// account marker or the final authenticated URL.
await page.GetByTestId("account-menu").WaitForAsync();

await page.GotoAsync("https://example.com/account/report");
await page.GetByRole(AriaRole.Heading, new() { Name = "Report" }).WaitForAsync();
await page.ScreenshotAsync(new() { Path = "authenticated.png" });

The login labels, button name, test ID, and heading are placeholders. Use locators that match the actual site and wait for a meaningful signed-in signal. Playwright documents Page.ScreenshotAsync and its options in the .NET Page API; the authentication guide explains state and login readiness patterns.

3. Save and restore authentication state

When repeated runs should reuse a login, save storage state only after authentication has completed and been verified. Restore it when creating a fresh browser context:

using Microsoft.Playwright;

var authFile = "playwright/.auth/user.json";

using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();

// First run: perform the site's login flow in this context and verify
// a stable signed-in condition before saving.
await using (var loginContext = await browser.NewContextAsync())
{
    var loginPage = await loginContext.NewPageAsync();
    await loginPage.GotoAsync("https://example.com/login");
    // Fill and submit the real login form here.
    await loginPage.GetByTestId("account-menu").WaitForAsync();
    await loginContext.StorageStateAsync(new() { Path = authFile });
}

// A later run: restore cookies and local storage into a new context.
await using var context = await browser.NewContextAsync(
    new() { StorageStatePath = authFile });
var page = await context.NewPageAsync();
await page.GotoAsync("https://example.com/account/report");
await page.GetByRole(AriaRole.Heading, new() { Name = "Report" }).WaitForAsync();
await page.ScreenshotAsync(new() { Path = "authenticated.png" });

This combined example shows the save and restore steps; in practice, login-state creation and screenshot capture are often separate program runs. The directory must exist before writing the state file. Add the auth directory to .gitignore and keep it out of build artifacts and shared logs. Playwright warns that saved state may contain sensitive cookies and headers that could be used to impersonate an account.

Know where the site stores authentication

Mechanism What to do
Cookies or local storage Use context storage state to save and restore them.
IndexedDB For Playwright .NET v1.51 and later, set IndexedDB = true when saving if the app stores auth tokens there: await context.StorageStateAsync(new() { Path = authFile, IndexedDB = true });
Session storage Storage state does not automatically persist session storage. Follow Playwright’s documented manual persistence and restoration approach for the application.

See the Playwright .NET release notes for the IndexedDB option and the authentication guide for storage-state details.

4. Choose screenshot options

The default screenshot captures the visible viewport. Use options to change the captured area or output:

// Capture the full scrollable page.
await page.ScreenshotAsync(new() { Path = "full.png", FullPage = true });

// Capture a specific element (the element must be present and visible).
await page.GetByTestId("report-chart").ScreenshotAsync(
    new() { Path = "chart.png" });

// JPEG with explicit quality, or PNG with a transparent background.
await page.ScreenshotAsync(new() { Path = "report.jpg", Type = ScreenshotType.Jpeg, Quality = 85 });
await page.ScreenshotAsync(new() { Path = "transparent.png", OmitBackground = true });

Other documented controls include a clip rectangle, scale, and animation handling. JPEG quality applies to JPEG output; PNG is lossless. Check the screenshot API reference for the complete option list and current signatures.

5. Make captures dependable

  • Wait for an observable condition. Wait for a final URL or stable signed-in element after login, then wait for a page-specific marker after navigation. A navigation event alone does not prove the app finished rendering.
  • Handle session expiry. Restored cookies or tokens can expire or be revoked. If the authenticated marker does not appear, run the login flow again and refresh the state file.
  • Use the right state boundary. Create a fresh context for isolation. If parallel tests change shared server-side data, use separate accounts to avoid conflicts; a shared account is suitable only when runs do not interfere.
  • Keep state private. Exclude storage files from version control and restrict access. Rotate or delete them when no longer needed.
  • Choose capture scope deliberately. Viewport screenshots are smaller and quicker to inspect; full-page screenshots include content below the fold and can be much taller. Lazy-loaded content may require scrolling or waiting for it to appear before capture.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its one-call API is useful for public pages; it does not perform your site’s private login flow or accept your Playwright storage state. Keep Playwright for pages that require account authentication.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation. ScreenshotNeo accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.

Other client examples

For a public page that does not require login, these examples call ScreenshotNeo. They are not substitutes for the authenticated Playwright flow above: never send passwords, session cookies, or storage-state contents to a screenshot endpoint.

Python

import requests

r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.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', new Uint8Array(await res.arrayBuffer()));

The Node.js snippet uses Bun’s file writer; on Node.js, replace the final line with import { writeFile } from 'node:fs/promises'; await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));. Check the docs for the available options and formats.

Troubleshooting

Symptom Likely cause Fix
Screenshot shows the login page Login did not complete, the session expired, or the protected URL redirected. Wait for a signed-in marker after login; inspect the final URL and refresh saved state if needed.
Storage-state restore appears unauthenticated The app uses IndexedDB or session storage, state was saved too early, or the token expired. Save after verifying login; enable IndexedDB = true where applicable; handle session storage separately; reauthenticate if expired.
Screenshot is blank or missing page content The page is still rendering, a resource failed, or the readiness signal was too weak. Wait for a meaningful element, check that it is visible, and investigate page or network errors before capture.
Element screenshot times out The locator matches no visible element or the element never becomes actionable/ready. Check the locator and wait for the element explicitly before taking its screenshot.
Full-page image is unexpectedly large The page has a long scroll area or content expands after loading. Use a viewport or element capture when that is the required output; confirm lazy content readiness before full-page capture.
Browser executable is missing Playwright’s browser binaries were not installed for the package/build. Run the generated Playwright install script for the selected browser and target configuration.

Performance, reliability, and cost

A screenshot requires browser startup, navigation, authentication, page readiness, and image encoding. Reusing a browser process can avoid repeated startup, while creating a fresh context per independent run preserves isolation. Full-page captures and high device scale factors produce larger images and can take longer to encode or transfer. Keep waits tied to actual page readiness rather than adding a large fixed delay to every run.

For reliability, make login and capture steps observable: record the final URL, wait for an authenticated marker, and handle expired sessions by renewing state. Avoid sharing one mutable account among parallel runs that change server-side data. Playwright itself is an automation library rather than a per-screenshot hosted service, so direct monetary cost depends on where the browser runs and the compute and storage you provide; the dossier does not establish a universal price or benchmark.

FAQ

Can I take the screenshot without logging in each time?

Yes. Save storage state after a successful login, then restore it into a new context. Reauthenticate when the site’s session expires.

Does StorageStatePath include every browser storage mechanism?

No. It covers supported cookies and local storage, with IndexedDB opt-in in .NET v1.51 and later. Session storage needs a separate persistence approach.

Can ScreenshotNeo capture my authenticated account page?

The provided ScreenshotNeo facts describe screenshots of URLs, not a workflow for signing into private accounts or importing Playwright state. Use Playwright for a page whose access depends on your login.