ScreenshotNeo

BlogHow-to

How to Capture Screenshots of Websites Behind a Login with Apify

Log in with Puppeteer or Playwright in an Apify Actor, wait for the protected page, capture it, and save the image to a key-value store.

By the ScreenshotNeo team4 October 20269 min read

To capture a website page behind a login with Apify, run a browser in an Apify Actor, complete the site’s authorized login flow, navigate to the protected page, wait until the content you need is visible, take a screenshot, and save the image to an Apify key-value store. The browser automation is the same basic workflow whether you use Puppeteer or Playwright; the login selectors, redirects, and extra authentication steps must match the target site.

This guide uses Puppeteer in an Apify Actor and includes a Playwright alternative. Use an account and pages you are authorized to access, and check the target site’s current rules. Authentication controls such as multifactor prompts and anti-automation checks are site-specific; this workflow does not bypass them.

1. Set up an Apify Actor

Create or open an Actor project configured for JavaScript and install the browser automation library supported by that project’s SDK and runtime. Apify’s screenshot example uses Puppeteer and says a similar example can be implemented with Playwright. Choose based on the library already supported by your project, the target site’s compatibility, and your existing session-handling code; the cited materials do not establish a universal winner.

Install and configure the Actor SDK and browser library using the instructions for the SDK version in your project. Apify’s documentation has examples from more than one SDK generation, so confirm the storage and lifecycle method names against the version you use.

2. Log in, navigate, capture, and save with Puppeteer

The following is a runnable workflow outline for an Actor after its dependencies and browser runtime are configured. Set the credentials and target URLs as Actor environment variables or secrets; do not put real credentials into source control. Replace the example selectors with selectors and readiness conditions for the site you are authorized to access.

import { Actor } from 'apify';
import puppeteer from 'puppeteer';

await Actor.init();

const loginUrl = process.env.LOGIN_URL;
const targetUrl = process.env.TARGET_URL;
const username = process.env.LOGIN_USERNAME;
const password = process.env.LOGIN_PASSWORD;

if (!loginUrl || !targetUrl || !username || !password) {
  throw new Error('Set LOGIN_URL, TARGET_URL, LOGIN_USERNAME, and LOGIN_PASSWORD');
}

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
  await page.goto(loginUrl, { waitUntil: 'domcontentloaded' });

  // These selectors are examples; inspect the site's actual login form.
  await page.locator('input[name="username"]').fill(username);
  await page.locator('input[name="password"]').fill(password);
  await Promise.all([
    page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
    page.locator('button[type="submit"]').click(),
  ]);

  // Verify successful authentication using a site-specific signal.
  await page.waitForSelector('[data-testid="account-menu"]', { timeout: 30000 });

  await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
  // Wait for the content that matters, not just the initial document load.
  await page.waitForSelector('[data-testid="report-content"]', { timeout: 30000 });

  const image = await page.screenshot({ fullPage: true, type: 'png' });
  await Actor.setValue('protected-page.png', image, {
    contentType: 'image/png',
  });
} finally {
  await browser.close();
  await Actor.exit();
}

The example waits for a navigation after clicking submit. Some applications authenticate through an in-page request and do not navigate. In that case, replace waitForNavigation with a wait for a reliable signed-in indicator, then verify the protected page loads as the authenticated user.

3. Playwright version

If your Actor uses Playwright, the sequence is the same. Create a browser context, log in, wait for a site-specific authenticated state, navigate to the protected URL, wait for the target content, and save the screenshot buffer. Configure Playwright and the browser in the Actor runtime according to the package versions in use.

import { Actor } from 'apify';
import { chromium } from 'playwright';

await Actor.init();

const loginUrl = process.env.LOGIN_URL;
const targetUrl = process.env.TARGET_URL;
const username = process.env.LOGIN_USERNAME;
const password = process.env.LOGIN_PASSWORD;

if (!loginUrl || !targetUrl || !username || !password) {
  throw new Error('Set LOGIN_URL, TARGET_URL, LOGIN_USERNAME, and LOGIN_PASSWORD');
}

const browser = await chromium.launch({ headless: true });
try {
  const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
  const page = await context.newPage();
  await page.goto(loginUrl, { waitUntil: 'domcontentloaded' });
  await page.locator('input[name="username"]').fill(username);
  await page.locator('input[name="password"]').fill(password);
  await page.locator('button[type="submit"]').click();
  await page.waitForURL(url => !url.toString().includes('/login'), { timeout: 30000 });
  await page.waitForSelector('[data-testid="account-menu"]', { timeout: 30000 });

  await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
  await page.waitForSelector('[data-testid="report-content"]', { timeout: 30000 });
  const image = await page.screenshot({ fullPage: true, type: 'png' });
  await Actor.setValue('protected-page.png', image, { contentType: 'image/png' });
} finally {
  await browser.close();
  await Actor.exit();
}

Check the Playwright and Actor SDK versions before using this code: runtime setup, browser binaries, and storage APIs can differ across versions. Apify’s documentation describes screenshot capture with Puppeteer and notes that the example is nearly the same with Playwright.

4. Reuse an authenticated session safely

Cookies can maintain authenticated status, so a flow may log in once, retrieve the browser context’s cookies, and restore them in a later context. In Playwright, cookies are associated with the browser context; in Puppeteer, retrieve them from the page or browser context using the API available in your installed version.

Store session state only when the site’s rules and your security requirements permit it. Treat cookies and saved browser state as credentials: restrict access, do not print them in logs, do not commit them to a repository, and define a retention period. Expired or revoked sessions must be refreshed through the normal login process. Some sites also bind sessions to other state, so cookies alone may not be sufficient.

5. Choose the right wait condition and screenshot options

A document load event does not guarantee that a single-page application, chart, lazy image, or report has finished rendering. Wait for a specific element or application state that proves the content to capture is ready. A fixed delay can be a fallback for known animation or rendering time, but it is less reliable than an observable readiness condition.

Need Approach
Initial HTML is available Navigate with a suitable load condition such as domcontentloaded.
Authentication completed Wait for a signed-in indicator or an expected URL, then verify protected content.
Specific protected content rendered Wait for the content selector or application state that matters.
Full long page Use full-page capture; confirm that lazy-loaded sections have appeared first.
Stable visual state Wait for animations, fonts, charts, or asynchronous data that affect the image.

Choose viewport dimensions to match the view you want. A viewport screenshot captures the visible area; a full-page screenshot captures the page’s full height, which can be large. Apify’s documented example uses a screenshot buffer and stores it as a file with an image content type.

6. Find and access the saved screenshot

The example writes the screenshot buffer to the Actor’s key-value store under protected-page.png and labels it image/png. Key-value stores are intended for arbitrary files and records. Access to stored resources depends on account settings and configured permissions, so check the actual resource access configuration before sharing a link or assuming the image is private.

For repeated captures, choose deterministic keys that identify the page or run without exposing sensitive account information. If a screenshot contains personal, financial, or confidential data, limit its access and retention accordingly.

7. Troubleshooting

Symptom Likely cause Fix
Login selector times out The site uses different field names, an iframe, or a changed login page. Inspect the authorized login page and update selectors; handle a frame explicitly if the form is embedded.
Submit click hangs waiting for navigation Login happens through an in-page request without navigation. Wait for a signed-in selector or authenticated application state instead of navigation.
Redirects back to login Credentials failed, the session was not established, or an additional step is required. Check the site’s normal login flow and handle permitted consent or multifactor steps. Do not attempt to bypass an access control.
Screenshot shows a login page The authentication check was too weak or the session expired before protected navigation. Assert a site-specific signed-in marker and a protected-page marker before capturing.
Screenshot is blank or incomplete Capture happened before client-rendered content or images were ready. Wait for the relevant content selector and any necessary image, chart, or lazy-load completion.
Screenshot is clipped or unexpectedly tall Viewport and full-page capture expectations differ, or the page uses nested scrolling. Set the desired viewport, test full-page behavior, and handle scrollable panels as separate elements if needed.
Browser fails to launch in the Actor Browser binaries or runtime dependencies do not match the configured package. Follow the browser setup for the Actor image and installed library version; inspect Actor logs for launch details without logging credentials.
Stored image cannot be retrieved The key differs from the expected name, content type is missing, or resource permissions restrict access. Confirm the stored key and metadata, then inspect the key-value store and access settings.

8. Performance, reliability, and cost

Each run must start or use a browser, authenticate if needed, load the protected page, wait for content, and encode an image. Reusing a valid session can avoid repeated login work, but it adds sensitive state to manage and may not work if the site expires or binds sessions. Full-page screenshots and high-resolution pages can consume more memory and time than a viewport capture.

For reliability, make readiness checks explicit, use bounded timeouts, and fail clearly when authentication or content checks do not pass. Avoid saving a plausible-looking screenshot when the page is actually a login form or error state. Do not claim a universal run time or success rate; those depend on the site, Actor configuration, and page content. Apify platform and Actor costs depend on the account and resource configuration; consult current Apify pricing and your usage rather than assuming a fixed cost per image.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It takes one GET request with a URL and returns an image or PDF; its [documentation](https://screenshotneo.com/docs/) lists the options. For a page that requires authentication, pass authorized cookies or headers using its request options; the browser setup above remains the right fit when you need to execute a custom login flow.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed; responses identify the page verdict and billing status.
  • An MCP server lets AI agents use screenshot, page-info, and PDF tools.
  • The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

How do I take a screenshot of a page after logging in?

Authenticate in a browser context, confirm the signed-in state, navigate to the protected URL, wait for its content, then call the browser page’s screenshot method and store the returned bytes.

Can Apify reuse login cookies in Playwright or Puppeteer?

Yes. Browser cookies can preserve authenticated status across contexts when restored appropriately, but they are sensitive, can expire, and may not be the only state the site requires.

Where does Apify save screenshots?

The documented pattern saves the screenshot buffer to a key-value store as an image file. The resource’s access depends on configured permissions.

Will the same login code work on every website?

No. Forms, selectors, redirects, multifactor flows, and authentication behavior vary by site. Adapt the code to the authorized target and validate that the protected content is visible before capture.