How to Capture a Logged-In Webpage After Two-Step Email Verification
Complete email verification, confirm the authenticated page, and save it as a full-page screenshot or PDF. Includes a reusable Playwright workflow.
To capture a page that requires two-step email verification, complete the site’s normal sign-in and verification flow first. Then confirm you are on the signed-in page and save a full-page screenshot or print it to PDF. For repeatable automation, use Playwright to complete the supported login once, save browser authentication state securely, and reuse it while the session remains valid.
Email verification is part of the site’s authentication. There is no universal way to obtain or automate every site’s verification code: open your own mailbox through its normal sign-in flow and enter the current code on the site’s verification screen. This guide covers the capture steps and a Playwright workflow without bypassing verification.
1. Sign in and complete email verification
- Open the target website and sign in with the account owner’s credentials through the site’s normal login page.
- When prompted, open the associated mailbox using its normal sign-in flow. Find the current verification message and enter the code on the target site’s verification screen.
- Wait for the site to finish any redirects or session setup. Confirm the destination page displays an authenticated-only element, such as an account menu or page heading.
- Navigate to the exact page you need to preserve. Check that its content has loaded and that it is still signed in before capturing.
Verification controls, code expiry, and delivery vary by site. If a code has expired, request a new one through the site’s interface. Do not treat an expired session as authenticated; sign in and verify again.
2. Capture a screenshot or PDF in Firefox
Save a full-page screenshot
- Open the authenticated page in Firefox.
- Open the browser’s screenshot tool and choose Save full page for the entire page, or Save visible for only the current viewport.
- Save the image and open it once to check that the page content and any important lower sections are present.
Firefox documents both full-page and visible-area screenshots in its screenshot help.
Save a PDF
- Open Firefox’s Print preview for the authenticated page.
- Inspect the preview, then choose Save to PDF as the destination.
- Open the saved PDF and check page breaks, images, and any content that matters to your use case.
A PDF is useful for a document-like record, but it may not look exactly like the live page: websites can apply different print styles. See Mozilla’s print help for the browser flow.
3. Automate repeat captures with Playwright
For repeatable capture, complete the site’s supported login and email verification in a real browser context, then save Playwright’s storage state. The example below assumes the site exposes a login form and a verification field. Adapt the selectors and steps to the site’s ordinary interface; when verification requires manual input, enter it in the browser and continue after the site accepts it.
Install Playwright and its Chromium browser:
npm init -y
npm install -D playwright
npx playwright install chromium
Save this as capture.mjs. Set LOGIN_URL, TARGET_URL, and the selectors to match the site. The script pauses at the verification prompt so you can enter the code through the site’s page, then waits for an authenticated-page marker before capturing.
import { chromium } from 'playwright';
import readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const LOGIN_URL = process.env.LOGIN_URL;
const TARGET_URL = process.env.TARGET_URL;
const USERNAME = process.env.SITE_USERNAME;
const PASSWORD = process.env.SITE_PASSWORD;
const AUTH_MARKER = process.env.AUTH_MARKER ?? '[data-testid="account-menu"]';
if (!LOGIN_URL || !TARGET_URL || !USERNAME || !PASSWORD) {
throw new Error('Set LOGIN_URL, TARGET_URL, SITE_USERNAME, and SITE_PASSWORD');
}
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
try {
await page.goto(LOGIN_URL, { waitUntil: 'domcontentloaded' });
await page.getByLabel(/email|username/i).fill(USERNAME);
await page.getByLabel(/password/i).fill(PASSWORD);
await page.getByRole('button', { name: /sign in|log in/i }).click();
// If the site asks for email verification, complete it in this browser window.
const rl = readline.createInterface({ input, output });
await rl.question('Complete any email verification in the browser, then press Enter here. ');
rl.close();
await page.goto(TARGET_URL, { waitUntil: 'domcontentloaded' });
await page.locator(AUTH_MARKER).waitFor({ state: 'visible', timeout: 30000 });
await page.screenshot({ path: 'authenticated-page.png', fullPage: true });
await context.storageState({ path: 'playwright/.auth/state.json' });
} finally {
await browser.close();
}
Run it with environment variables set in your shell. Avoid putting passwords directly into the script or committing them to source control. For sites with different form labels, replace the label and button locators with selectors appropriate to that site’s interface.
LOGIN_URL='https://example.com/login' \
TARGET_URL='https://example.com/account' \
SITE_USERNAME='you@example.com' \
SITE_PASSWORD='use-a-secret-manager' \
AUTH_MARKER='[data-testid="account-menu"]' \
node capture.mjs
This example captures a PNG and saves state for later runs. To reuse the saved state, create the context with storageState and still verify the authenticated marker before navigating or capturing:
const context = await browser.newContext({
storageState: 'playwright/.auth/state.json'
});
Playwright’s authentication guide recommends checking a final URL or visible signed-in UI because login may involve redirects and delayed cookie setting. Its storage state can contain cookies and headers that could impersonate the account. Keep the file out of version control, restrict access, and refresh or delete it when no longer needed. Add playwright/.auth/ to .gitignore.
Storage-state limits
- Storage state supports cookies and local storage; IndexedDB support and other options depend on the installed Playwright version. Check the BrowserContext API for the version you use.
- Session storage is not included in the normal persisted snapshot. If the site depends on it, follow Playwright’s documented session-storage setup rather than assuming the saved file contains it.
- Authentication state can expire, be revoked, or be tied to a browser, device, or network. If the signed-in marker does not appear, complete login and verification again.
4. Capture with Chrome Headless
Chrome Headless can save a screenshot or PDF from a browser session. The command-line capture flags do not perform email verification for you; first establish a legitimate authenticated browser session and make sure the headless run can use it. Chrome documents --screenshot, --print-to-pdf, and --timeout in its Headless Chrome guide.
chrome --headless --timeout=5000 --screenshot=page.png 'https://example.com/account'
chrome --headless --timeout=5000 --print-to-pdf=page.pdf 'https://example.com/account'
These basic commands open a URL without supplying account state. For a protected page, use an appropriately configured browser profile or another supported authenticated context, protect its data, and verify that the output actually shows the signed-in page. The timeout is a maximum wait before capture; it does not guarantee that a slow page or delayed application content has finished loading.
5. Choose the right capture format
| Output | Use it when | Check |
|---|---|---|
| Full-page screenshot | You need the visual layout, interface, or page as rendered in the browser. | Confirm content below the initial viewport loaded and appears in the image. |
| Visible-area screenshot | You need a specific viewport or the currently visible state. | Scroll to the intended section and confirm the viewport before saving. |
| You need a document-like copy that is easy to print or retain. | Inspect print preview and the saved file because print styling and page breaks can differ from the live site. | |
| Automated screenshot or PDF | You need repeatable output in a developer workflow. | Confirm authentication, wait for meaningful page readiness, and protect saved browser state. |
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The page redirects to login. | Verification did not finish, the session expired, or the saved state is incomplete. | Complete the normal login and email flow again. Wait for a signed-in marker before capture; refresh the state file only after successful authentication. |
| The code is rejected or no longer works. | The code may be stale, already used, or associated with a different login attempt. | Request a current code using the site’s interface, check the correct mailbox, and enter it in the active verification flow. |
| Playwright times out waiting for the account marker. | The selector may not match, the page may not be authenticated, or sign-in setup may still be running. | Inspect the page in headed mode, correct AUTH_MARKER, and wait for the site’s actual signed-in element or final URL. Do not use a sleep as proof of authentication. |
| The screenshot is blank or missing content. | The page may still be loading, lazy content may not have appeared, or the capture ran before navigation completed. | Wait for a specific content selector, scroll relevant areas into view if needed, and capture after the visible content is ready. |
| The PDF looks different from the webpage. | The site may use print-specific styles or pagination. | Review print preview, adjust the browser print settings if available, or use a screenshot when matching the on-screen appearance matters. |
| Reused storage state no longer works. | Cookies or tokens expired, were revoked, or depend on session storage or other browser properties. | Repeat the supported login and verification, save fresh state, and check whether the site requires session storage or a persistent profile. |
7. Performance, reliability, and cost
A manual browser capture has no service API charge, but it takes a person to complete verification and inspect the result. Automated capture reduces repeated manual steps after a legitimate login, though it still depends on the site’s session lifetime, changing selectors, page load behavior, and email challenge policy.
For reliability, wait on observable conditions: the final URL, an authenticated-only element, and the content you intend to capture. A fixed delay can be too short on a slow response and unnecessarily long on a fast one. Keep the authenticated browser profile or storage-state file private, limit who can read it, and remove it when the work is done. Do not include credentials, verification codes, cookies, or state files in logs or screenshots.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A normal screenshot API call cannot complete a site’s interactive email verification or use your private Playwright state, so use this option for pages that are publicly reachable to the API. It returns a screenshot or PDF from one GET request. See the ScreenshotNeo API documentation.
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 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. Responses identify the page verdict and billing status in headers.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents and 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 to get 1,000 screenshots a month with no card.
FAQ
Can I capture the page without completing email verification?
No. Complete the site’s normal verification flow before capturing the authenticated page. This guide does not provide a way to bypass it.
Does a screenshot include the login credentials or verification code?
Only if those details are visible in the captured page or browser UI. Capture the destination page after verification, and avoid including sensitive fields or browser chrome in the output.
Will Playwright’s saved state work indefinitely?
No. Authentication state can expire or be revoked, and some sites depend on session storage or other browser properties. Confirm the authenticated marker each time before capturing.
Should I choose an image or PDF?
Choose an image to preserve the browser’s visual rendering. Choose PDF for a document-like copy, then inspect the print result for layout differences.


