ScreenshotNeo

BlogHow-to

Fix Screenshot API Captures With a Consent Dialog Covering the Page

A consent prompt can be a browser dialog or a page overlay, and each needs a different fix. Diagnose it, handle it in Playwright, and choose the right screenshot scope.

By the ScreenshotNeo team4 October 20269 min read

If a screenshot API captures a consent prompt over the page, first determine whether it is a browser-native JavaScript dialog or a consent overlay rendered inside the page. A native alert, confirm, or prompt is handled through Playwright’s dialog event. A DOM overlay is page content: locate it and use the consent controls that match your workflow before taking the screenshot. Masking or hiding an overlay changes the image; it does not make a consent choice.

The examples below use Playwright because the title does not identify a particular screenshot provider. If you use another API, look for its documented browser interaction or pre-capture hooks rather than assuming Playwright code will work there. See the official Playwright Page API and screenshot guide.

1. Diagnose what is covering the page

Look at the prompt and inspect the page or automation trace. A native JavaScript dialog is browser UI triggered by JavaScript, such as alert(), confirm(), or prompt(). A consent banner or modal that appears in the page’s DOM is a regular web element, even when it covers the whole viewport.

What you see Likely type Use
A browser dialog with a message and browser-provided buttons Native JavaScript dialog Playwright’s dialog event
A styled panel, backdrop, banner, or modal in the page DOM overlay Find its locator and handle its controls
A dialog that appears only on some runs Often an intermittent overlay Consider an overlay handler if it blocks an action

Do not assume every site uses the same consent vendor, text, selector, or button labels. Prefer a stable accessible role and name where available; otherwise use a selector specific to the site you control or have inspected.

If the prompt appears predictably, wait for it and click the intended site-provided choice before capturing. Choose accept, reject, or manage preferences according to the state your workflow is meant to represent. The example uses accessible button names; replace them with the real names on the target site. It deliberately does not guess a universal consent selector.

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

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });

  try {
    await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });

    // Replace the role/name with the consent control actually shown by the site.
    const consentDialog = page.getByRole('dialog', { name: 'Privacy choices' });
    await consentDialog.waitFor({ state: 'visible', timeout: 10000 });
    await consentDialog.getByRole('button', { name: 'Reject optional cookies' }).click();
    await consentDialog.waitFor({ state: 'hidden', timeout: 10000 });

    await page.screenshot({ path: 'page.png' });
  } finally {
    await browser.close();
  }
})();

For a site whose prompt is a banner rather than an accessible dialog, identify that element using the inspected DOM and wait for it explicitly. For example:

const banner = page.locator('[data-testid="consent-banner"]');
await banner.waitFor({ state: 'visible', timeout: 10000 });
await page.getByRole('button', { name: 'Reject optional cookies' }).click();
await banner.waitFor({ state: 'hidden', timeout: 10000 });

Use a locator that identifies the actual consent UI, not a broad locator that could match unrelated page elements. If the consent control is inside an iframe, locate the correct frame and interact with its controls. If the panel never appears, the site may already have a stored preference or may display the prompt only under certain conditions; make the test context and consent state explicit.

3. Handle native JavaScript dialogs separately

Register a listener before the code that could trigger the dialog. A listener must accept or dismiss the dialog; otherwise the page can remain blocked. With no page or browser-context dialog listeners, Playwright automatically dismisses JavaScript dialogs. That behavior does not dismiss a DOM consent modal.

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

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // Install before navigation or the action that may trigger a dialog.
  page.on('dialog', async dialog => {
    console.log(`JavaScript dialog: ${dialog.type()} — ${dialog.message()}`);
    if (dialog.type() === 'confirm') {
      await dialog.dismiss(); // Change to accept() only if that is intended.
    } else if (dialog.type() === 'prompt') {
      await dialog.dismiss();
    } else {
      await dialog.dismiss();
    }
  });

  try {
    await page.goto('https://example.com');
    await page.screenshot({ path: 'page.png' });
  } finally {
    await browser.close();
  }
})();

For a prompt dialog that must receive text, call dialog.accept('value') with the intended input. Handle beforeunload dialogs only where the navigation flow requires it. Avoid blanket acceptance unless accepting is the correct behavior for the test.

4. Use an overlay handler only for unexpected blockers

page.addLocatorHandler() is useful when an unpredictable overlay appears and blocks a later action. Playwright checks the trigger before actions requiring actionability checks and before auto-waiting assertions. It is not a background watcher: if the overlay appears and the script performs no relevant action or assertion, the handler does not run. The handler is available starting with Playwright v1.42.

// Example for an intermittent newsletter overlay, not a predictable consent step.
await page.addLocatorHandler(
  page.getByRole('dialog', { name: 'Subscribe to updates' }),
  async () => {
    await page.getByRole('button', { name: 'No thanks' }).click();
  }
);

await page.goto('https://example.com');
await page.getByRole('link', { name: 'Documentation' }).click();
await page.screenshot({ path: 'page.png' });

After the handler runs, Playwright waits for the triggering overlay to become hidden unless configured otherwise. Keep handler actions self-contained because the handler may run between steps that assume a stable page state. For an expected consent prompt, the explicit wait-and-choice flow is easier to read and makes the resulting state clear.

5. Decide whether the screenshot should preserve, mask, or dismiss the prompt

  • Test the visitor flow: interact with the consent controls and capture the resulting state.
  • Document the consent UI: leave it visible and capture it.
  • Hide sensitive or distracting pixels in a controlled capture: use a mask or screenshot CSS, and describe the image as altered.

Playwright’s screenshot mask option covers matched locator bounds with a solid color (pink by default); maskColor changes that color. The screenshot style option applies CSS during capture. These options change representation only: they do not click a consent choice, save preferences, or establish the site’s consent state. Check the API reference and installed Playwright version before using version-specific options; the docs identify style as added in v1.41 and maskColor in v1.35.

// Intentional visual mask: the website's consent state is unchanged.
await page.screenshot({
  path: 'masked.png',
  mask: [page.getByRole('dialog', { name: 'Privacy choices' })],
  maskColor: '#777777'
});

// Intentional capture-only styling. Verify the selector against this site.
await page.screenshot({
  path: 'styled.png',
  style: '[data-testid="consent-banner"] { visibility: hidden !important; }'
});

Capture CSS that hides the prompt is not a consent action. It can also hide evidence of the underlying state, so use it only when a transformed visual is the goal.

6. Choose screenshot scope separately

The default page screenshot captures the visible viewport. fullPage: true captures the full scrollable page, and a locator screenshot captures one element. Changing scope does not resolve an overlay: a full-page capture can still include it, while an element capture can be visually obscured by overlapping page content.

// Viewport
await page.screenshot({ path: 'viewport.png' });

// Full scrollable page
await page.screenshot({ path: 'full-page.png', fullPage: true });

// One element
await page.locator('main article').screenshot({ path: 'article.png' });

// Return image bytes for downstream processing
const pngBytes = await page.screenshot();

Pick the scope after handling the prompt. For repeatable visual comparisons, keep viewport size, browser, page state, and consent choice consistent between runs.

7. Adapt the sequence to another screenshot API

The title does not name a provider, so the Playwright calls above are not universal API parameters. For a hosted screenshot API, check whether it supports navigation actions, selectors, clicks, waits, cookies, or pre-capture scripts. Map the same sequence: load the page, identify the prompt, perform the intended interaction, wait for the resulting state, then capture. If the provider only accepts a URL and capture options, it may not be able to interact with that site’s specific consent UI; verify its documented behavior instead of assuming a CSS option accepts consent.

8. Troubleshooting

Symptom Likely cause Fix
The dialog listener never sees the consent panel The prompt is a DOM overlay, not a native JavaScript dialog Inspect the DOM and use a locator for the panel and its actual controls.
The page hangs when a native dialog appears A registered dialog listener does not accept or dismiss it Resolve every dialog in the listener, including any prompt input case.
addLocatorHandler did not run No triggering actionability check or auto-waiting assertion occurred, or the locator did not match Use an explicit wait for predictable UI; verify the handler locator for intermittent blockers.
Consent locator times out Wrong accessible name/selector, delayed UI, iframe, or prompt absent due to stored state Inspect the page and frame, use the real accessible label, and define the initial storage state.
Click times out or says the target is not actionable Another element covers it, it is hidden, or the locator matches multiple controls Target the visible control within the correct dialog and wait for it to be actionable.
The prompt is still visible in a full-page capture Full-page changes capture extent, not page state Handle or intentionally mask the prompt before choosing viewport or full-page scope.
Masking hides the panel but the page still behaves as unconsented A mask changes pixels only Use the website’s consent action when the underlying state matters.
Screenshot option is rejected or missing Installed Playwright version lacks that option Check the version annotation in the API docs and upgrade or use supported options.

9. Performance, reliability, and cost

For reliable captures, prefer explicit state transitions over fixed sleeps: wait for the prompt, click the chosen control, then wait for it to hide or for a known page state to appear. A fixed delay can waste time when the page is fast and still fail when it is slow. Set sensible navigation and locator timeouts, and save diagnostic context when a capture fails. Keep context storage deliberate: persisted cookies may suppress a banner in later runs, while a fresh context may show it again.

Full-page screenshots and high-resolution output involve more rendered pixels than a viewport capture, and waiting for page resources or animations can add time. Capture only the scope and readiness state the job needs. Playwright is self-managed browser automation, so account for the runtime and maintenance of the browser environment; no benchmark or universal cost figure applies to every deployment. Hosted screenshot APIs instead have provider-specific limits and billing rules, which should be checked in their documentation.

Or skip the browser setup

If your goal is a clean page capture without maintaining browser automation, ScreenshotNeo is a website screenshot API and MCP server. Its cleanup accepts cookie/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 and CAPTCHAs, blank pages, timeouts, failed loads, 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 Claude, Cursor, and other MCP clients.

Make one GET request (replace the URL with your target). See the ScreenshotNeo API documentation for options and setup.

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}`);
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())));

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed; its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for free and get 1,000 screenshots a month with no card.

FAQ

Does a screenshot mask accept cookies?

No. It covers pixels in the output. Use the site’s consent controls if the page state must reflect a choice.

Should I accept or reject the prompt in an automated capture?

Use the choice that matches the purpose and expected state of your workflow. The code should not silently choose a preference that the workflow does not intend.

No. Full-page capture changes the screenshot extent; handle the overlay separately.

Can I use the Playwright locator handler for any screenshot provider?

No. It is a Playwright API. For another provider, use only its documented interaction and pre-capture features.

References