ScreenshotNeo

BlogHow-to

How to Monitor a Website Screenshot After Logging In

Use Playwright to sign in, capture a protected page, and compare it with an approved visual baseline. Includes secure auth-state handling and a ScreenshotNeo alternative.

By the ScreenshotNeo team4 October 20269 min read

To monitor a page that requires login, use browser automation to sign in with an authorized test account, save the authenticated browser state securely, and reuse it for scheduled captures. Verify that the protected page actually loaded, wait for stable content, take a screenshot, and compare it with a reviewed baseline. A screenshot alone is only a capture; monitoring requires repeat captures and a process for reviewing changes.

Choose a monitoring approach

Playwright Test with local screenshot baselines gives you direct control over the authenticated browser context and capture environment. Your team runs the browser job, stores the baseline, protects authentication state, and reviews diffs.

Hosted visual testing can provide capture review and browser coverage. Check where rendering happens and how authentication is configured. For example, Percy documents a workflow where captured DOM state and assets are later rendered in Percy infrastructure; protected assets may need authentication configured for that separate stage. Review data handling before sending protected page content to a service. [Percy documentation] [Percy visual testing overview]

Applitools Eyes is another visual testing option with Playwright integration. Verify the exact authenticated workflow, baseline behavior, current capabilities, and commercial terms for your setup. [Applitools Playwright documentation]

Compare approaches on credential handling, where pages render, browser and operating system coverage, baseline review, alerting, retention, and maintenance. The references here establish product workflows, not a current pricing comparison.

Build an authenticated screenshot monitor with Playwright

The example below uses Playwright Test. It has two parts: a one-time or occasional setup that signs in and saves browser state, and a monitoring test that loads that state, verifies the protected route, and compares a screenshot with its stored expectation.

1. Install Playwright

npm init playwright@latest

Choose JavaScript or TypeScript when prompted. The following examples use JavaScript. Run the setup in an environment that can reach your site and complete its supported sign-in flow. Use an account authorized for monitoring and limit its permissions to what the check needs.

2. Create the authentication setup

Create tests/auth.setup.js. Update the login URL, selectors, and environment variable names for your application. This example expects a test account username and password in environment variables; keep them out of source code.

const { test: setup, expect } = require('@playwright/test');
const path = require('path');

const authFile = path.join(__dirname, '.auth', 'user.json');

setup('sign in and save browser state', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('Email').fill(process.env.MONITOR_USERNAME);
  await page.getByLabel('Password').fill(process.env.MONITOR_PASSWORD);
  await page.getByRole('button', { name: 'Sign in' }).click();

  // Wait for a marker that appears only after successful sign-in.
  await expect(page.getByTestId('account-menu')).toBeVisible();
  await page.context().storageState({ path: authFile });
});

If your login requires multi-factor authentication or another interactive approval, arrange an authorized test flow with the site owner. Do not attempt to bypass that control. Some applications require extra steps such as accepting terms or choosing a workspace; include those steps and assert a signed-in marker before saving state.

3. Configure the setup project and protected-page test

Create or update playwright.config.js. The setup project runs before the monitor project so the saved state exists. The state file is placed under tests/.auth; add that directory to .gitignore.

const { defineConfig } = require('@playwright/test');

module.exports = defineConfig({
  testDir: './tests',
  projects: [
    {
      name: 'setup',
      testMatch: /auth\.setup\.js/
    },
    {
      name: 'monitor',
      dependencies: ['setup'],
      testMatch: /monitor\.spec\.js/,
      use: {
        baseURL: 'https://example.com',
        storageState: 'tests/.auth/user.json',
        viewport: { width: 1440, height: 1000 }
      }
    }
  ],
  expect: {
    toHaveScreenshot: {
      animations: 'disabled',
      caret: 'hide'
    }
  }
});
tests/.auth/

Keep the authentication state out of Git and restrict access to it in CI or your monitoring environment. Playwright warns that saved state can include cookies and headers that let someone impersonate the account. Treat it like a credential. [Playwright authentication]

4. Capture and compare the protected page

Create tests/monitor.spec.js. Replace the route and marker with a page-specific path and element. The marker prevents a redirect to a login page from being mistaken for a valid screenshot.

const { test, expect } = require('@playwright/test');

test('account dashboard matches its visual baseline', async ({ page }) => {
  await page.goto('/account/dashboard');

  // Fail clearly if the session expired or access was denied.
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  await expect(page.getByTestId('dashboard-ready')).toBeVisible();

  // Compare the selected viewport with the committed reference image.
  await expect(page).toHaveScreenshot('dashboard.png', {
    fullPage: false,
    animations: 'disabled',
    caret: 'hide'
  });
});

Playwright’s screenshot assertion waits until two consecutive screenshots match before comparing with the expected image. On the first run, create the baseline in a trusted, reviewed environment:

npx playwright test --project=monitor --update-snapshots

Then run the monitor normally:

npx playwright test --project=monitor

Review the generated diff when a check fails. Update the baseline only after confirming that the visual change is intentional. Playwright compares against stored expected screenshots and provides screenshot assertion controls. [Playwright screenshot assertions] [Playwright visual comparisons]

5. Capture only the scope you need

For a specific panel, assert against a locator instead of the whole page:

await expect(page.getByTestId('revenue-panel')).toHaveScreenshot('revenue-panel.png');

For a full-page capture, use fullPage: true in the page screenshot assertion options:

await expect(page).toHaveScreenshot('dashboard-full.png', { fullPage: true });

Choose a viewport screenshot when the question is what a user sees without scrolling. Use an element screenshot to isolate a component. A full-page capture covers content below the fold, but long pages and sticky or lazy-loaded elements can make the capture less representative unless you first scroll or wait for the content you care about. Playwright documents page and element screenshot options and their constraints. [Playwright screenshots]

Stabilize captures and review changes

Visual differences can come from genuine regressions, normal content changes, or rendering changes. Make captures repeatable before interpreting a diff:

  • Keep browser version, operating system, viewport, and rendering settings consistent where possible. Playwright cautions that these affect screenshot output. [Playwright visual comparisons]
  • Wait for meaningful page content or an application ready marker rather than relying only on a fixed delay.
  • Disable animations and hide the caret when they add noise. Mask or hide only content that is irrelevant and expected to change, such as a timestamp. Do not mask the information the check is meant to protect.
  • Review changed screenshots before updating the approved reference. A visual diff is not automatically an outage.
  • Choose the monitoring interval, alert destination, and screenshot retention based on the page’s importance and your operations process. The cited visual testing documentation does not prescribe a universal schedule or retention period.

For example, mask a dynamic timestamp while preserving the rest of the dashboard:

await expect(page).toHaveScreenshot('dashboard.png', {
  mask: [page.getByTestId('last-updated')],
  animations: 'disabled',
  caret: 'hide'
});

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. For a page that can be captured with a normal request, one GET call returns an image or PDF. It does not perform your site’s login flow or reuse Playwright’s authenticated browser state, so the browser workflow above remains necessary for pages that require a signed-in session.

For a publicly accessible page, this cURL example saves a WebP capture. See the ScreenshotNeo API documentation for request options and authentication details.

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

There is also a Python example:

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)

And Node.js:

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 import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
  • 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 provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.

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

Troubleshooting

Symptom Likely cause Fix
The monitor shows the login page or fails its page marker The saved session expired, setup did not finish signing in, or the account lacks access. Re-run the authorized setup, assert a signed-in marker before saving state, and confirm the monitoring account can open the route.
Playwright cannot find the login field or button The example labels or accessible names do not match the application. Use the actual label, role, or stable test ID. Prefer accessible locators over brittle positional selectors.
The screenshot fails on every run with small differences Browser or host rendering differs, animation or caret state changes, or dynamic content is visible. Standardize the browser environment, disable animation, hide the caret, wait for readiness, and narrowly mask irrelevant changing regions.
A full-page image misses content loaded while scrolling Some pages load images or sections lazily. Scroll the relevant sections into view and wait for their content before capture, or monitor the specific element instead.
The baseline changes unexpectedly in CI The baseline was regenerated without review, or the CI rendering environment differs from the approved environment. Keep baseline updates explicit and reviewed; align browser, OS, and viewport settings.
The saved state file is missing The setup project did not run, its output path differs, or the auth directory is not available to the job. Check project dependencies and paths; ensure the setup job writes the state before the monitor job reads it.
Interactive login cannot run unattended The site requires MFA or human approval. Work with the site owner on an authorized test account and supported test authentication arrangement.

Security, reliability, and cost

Protect the authenticated state

A storage-state file can contain cookies and headers that impersonate the account. Exclude it from source control, restrict who and what can read it, store it as a secret in CI, and regenerate it when the session expires. Use an account with only the permissions required for monitoring. Do not assume a session lasts indefinitely; sites set their own expiration and reauthentication requirements. [Playwright authentication]

Separate visual changes from service failures

Report authentication failures, navigation timeouts, missing readiness markers, and screenshot mismatches as distinguishable outcomes. A visual diff can reflect changed data, ads, avatars, timestamps, animation, or a browser update rather than an outage. Keep enough run context to investigate, and require review before accepting a new baseline.

Account for runtime and spend

Each monitoring run launches or uses a browser, performs sign-in-state loading, navigates, waits for readiness, and captures an image. The browser job and screenshot storage are operational costs for a self-managed setup; the exact cost depends on your infrastructure and schedule. Reuse authenticated state while valid, avoid needless full-page captures, and set timeouts based on the page’s behavior. Hosted services can reduce some review and browser-management work, but assess their current pricing and authenticated data flow directly; the sources cited here do not establish current prices.

Frequently asked questions

Can I monitor a page that requires a one-time password?

Only use an authorized test arrangement supported by the site owner. If sign-in needs an interactive approval, do not try to bypass it; arrange a suitable test account or authentication workflow.

Does a screenshot comparison prove the site is healthy?

No. It checks visual output against a reference. Pair it with separate checks for availability and application behavior if those matter to your monitoring goal.

When should I update the screenshot baseline?

After reviewing the difference and confirming the UI change is intended. Do not automatically accept every new screenshot as the reference.

Can ScreenshotNeo capture my authenticated dashboard with this API call?

The one-call example is for a page accessible to the capture request. This article’s Playwright method handles a browser sign-in flow and saved authenticated state; use it when the page requires that session.