How to Handle Cookie Consent in Screenshot APIs
Choose whether a screenshot should show a consent banner, record a visitor’s choice, or omit the overlay for a clean image—and make that state repeatable.
Decide what the screenshot needs to represent before changing the page. For a first-visit audit, leave the cookie banner visible. To test a visitor’s choice, interact with the actual consent controls and verify the resulting state. For a clean visual asset, suppress or style out the banner and label the capture as modified. Hiding a banner is not the same as recording consent.
This distinction matters because a screenshot records pixels and browser state at one moment. It does not establish that a visitor made a choice or that tracking is authorized. The European Data Protection Board says that when consent is used as a legal basis for processing personal data, it must be “freely given, informed, specific and unambiguous.” The right legal basis depends on jurisdiction and processing context; a screenshot API cannot decide that for you. Read the EDPB’s small-business guide.
Choose the capture state first
| Goal | What to do | What the screenshot means |
|---|---|---|
| First-visit audit or bug report | Start with a fresh browser context and capture the banner as it appears. | The page state presented before a consent choice in that context. |
| Test a consent choice | Click the real accept, reject, or preference control; verify the expected result; then capture. | The state after that interaction, within the tested browser context. |
| Clean documentation or marketing image | Use a provider’s banner suppression option or controlled screenshot styling. Record that suppression was used. | A visually modified capture. It does not prove that a visitor made a choice. |
For repeatable tests, record the starting state, consent action, wait condition, and whether the final image was modified. Keep the goal explicit: fidelity to a visitor view, verification of a choice, or a clean visual.
DIY with Playwright
Playwright is useful when you need browser-level control: a fresh context, actual interaction with a consent control, and a screenshot after a verifiable state change. The examples below use Node.js and Playwright’s documented Page API. Install it with npm install playwright and install a browser with npx playwright install chromium. Save the script as consent-shot.mjs, then run node consent-shot.mjs.
Capture a first-visit banner
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
const page = await context.newPage();
try {
await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
// Replace this selector with a stable selector from the site under test.
const banner = page.locator('[data-testid="cookie-banner"]');
await banner.waitFor({ state: 'visible', timeout: 10000 });
await page.screenshot({ path: 'first-visit.png', fullPage: true });
} finally {
await context.close();
await browser.close();
}
Use a fresh context so a prior cookie or local-storage choice does not silently turn this into a returning-visitor capture. Replace the example selector with one that identifies the banner on the target site. If the site has no stable test selector, assert a reliable visible text or role-based locator instead.
Record a real choice before capture
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
const page = await context.newPage();
try {
await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
const banner = page.locator('[data-testid="cookie-banner"]');
await banner.waitFor({ state: 'visible', timeout: 10000 });
// Choose the action that matches the test case; do not assume Accept is the right choice.
await page.getByRole('button', { name: 'Reject optional cookies' }).click();
// Verify the state expected by this site and test. Disappearance alone may not prove
// that preferences were saved, so check a confirmation or persisted state where possible.
await page.getByRole('status').filter({ hasText: 'Your preferences have been saved' }).waitFor({ state: 'visible', timeout: 10000 });
await banner.waitFor({ state: 'hidden', timeout: 10000 });
await page.screenshot({ path: 'after-choice.png', fullPage: true });
} finally {
await context.close();
await browser.close();
}
Consent UI varies by site. The role, label, confirmation text, and persistence check above are examples to replace with the actual interface. If a button opens a preference center, test and verify the intended setting there. Preserve the action and browser context in test records so someone can reproduce the capture.
Use screenshot-time styling for a visual-only clean image
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
try {
await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.screenshot({
path: 'clean-visual-only.png',
fullPage: true,
style: '[data-testid="cookie-banner"] { display: none !important; }'
});
} finally {
await browser.close();
}
Playwright’s screenshot style option applies styles during capture and supports piercing Shadow DOM and inner frames. This changes the image’s appearance; it does not click a control or save a choice. Use a stable, narrowly scoped selector so the rule does not hide unrelated page content. For visual comparisons, Playwright also supports masking selected elements. Choose masking when you need to obscure or normalize a region rather than remove it from the rendered layout.
Capture options that affect consent tests
- Navigation and readiness:
page.goto()supportswaitUntilstates such asdomcontentloaded,load, andnetworkidle. Choose a state that matches the site; network activity can continue indefinitely on pages with polling or analytics. - State-specific waits: wait for the banner to become visible before a first-visit capture, or wait for the site’s confirmation and resulting UI state after a choice. A short fixed sleep can race the banner or waste time.
- Context isolation: use a new browser context for an independent first visit. Reuse a context only when deliberately testing returning-visitor persistence.
- Image scope: use
fullPage: truefor the full document or omit it for the viewport. Use a fixed viewport for comparable runs. - Pixel scale: Playwright can capture in CSS-pixel or device-pixel scale. Keep scale consistent when comparing screenshots.
- Overlay timing: consent interfaces may load asynchronously or appear after another prompt. Wait for the state relevant to the test rather than relying only on a brief fixed delay.
See the Playwright Page API for the current navigation and screenshot options. Its documentation also cautions that overlays can appear at different times and complicate automated tests.
Hosted screenshot APIs and banner controls
Some hosted screenshot APIs expose a cookie-banner setting. For example, the Screenshot API REST documentation documents blockCookieBanners, along with wait controls such as waitUntil, waitForSelector, and delayMs. ScreenshotAPI.com’s reference also documents blockCookieBanners and describes it as enabled by default. Provider options and defaults can change, so check the current reference and explicitly set the behavior you want.
“Block” and “dismiss” are vendor feature labels, not legal conclusions. A provider’s public reference may not explain whether the implementation clicks a control, hides an element, filters it, or uses another method. Do not assume suppression records a choice, always works, or establishes compliance. Test representative target pages and label the resulting state accurately.
When evaluating a hosted API, check whether it can:
- Show the banner unchanged for a first-visit audit.
- Wait for a selector or other stable condition before capturing.
- Interact with consent controls when the test requires a real choice, and expose enough browser state to verify the result.
- Suppress banners for clean imagery, with behavior and defaults documented.
- Return a clear failure when the page or requested state does not load.
For a vendor-specific request, use that provider’s documented parameter names and authentication. Do not copy a suppression parameter from one API to another. If you need to compare services, compare their current documentation and run the same target pages against each; this research does not establish a universal success rate or implementation method.
Make consent captures repeatable and auditable
- Write the expected initial state. For example: fresh context, no saved site preference, banner expected to appear.
- Define the action. State whether no action is taken, a specific real control is clicked, or visual suppression is enabled.
- Wait for the matching condition. Assert banner visibility, a saved-preference confirmation, or a stable page selector as appropriate.
- Capture at a fixed viewport and scale. Keep image dimensions and full-page behavior consistent between runs.
- Record capture metadata. Include target URL, timestamp, browser/context setup, action taken, wait condition, and whether styles or suppression modified the result.
- Review failures as state failures. A missing banner may mean consent was persisted, the banner failed to load, the selector changed, or the page did not finish loading. Do not treat every absent overlay as proof of a successful choice.
A clean screenshot is suitable for a clean visual deliverable. For consent UI QA, compliance evidence, or reproducing a visitor report, preserve the relevant state and explain any modifications. A screenshot can support a test record, but it cannot by itself prove the site’s legal basis or the visitor’s intent.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its API accepts one GET request with a URL and returns PNG, JPEG, WebP, or PDF. For a clean visual, it accepts cookie/consent banners like a visitor and removes more than 60 known consent platforms, along with newsletter popups and chat widgets; each step can be turned off. For a first-visit audit or a real consent-choice test, turn off the relevant cleaning behavior and use browser automation that can verify the actual state. Suppression is visual cleanup, not proof of consent.
Read the ScreenshotNeo API documentation. Example request for a clean capture:
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 Bun.write('shot.webp', res);
The Node.js example uses Bun’s file writer to save the response. In Node.js, save it with:
import { writeFile } from 'node:fs/promises';
const bytes = Buffer.from(await res.arrayBuffer());
await writeFile('shot.webp', bytes);
- Cookie banners, popups, and chat widgets are removed before the shot; each cleaning step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify page verdict and billing through
X-Page-VerdictandX-Billedheaders. - An MCP server provides
take_screenshot,get_page_info, andcapture_pdffor Claude, Cursor, and other MCP clients. - 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 for 1,000 screenshots a month with no card.
Performance, reliability, and cost
Performance
Browser automation gives direct control over navigation, interaction, and state verification, but it requires a browser process and page load for each isolated run. Keep browser contexts scoped to the test, use state-based waits, and avoid waiting for network idle when persistent network traffic makes it unreachable. A fixed short delay is fast only when it does not miss the UI; a selector wait is usually a clearer expression of the requirement.
A hosted API avoids managing browser installation and capture infrastructure in your application. Its actual latency and throughput depend on the provider, target site, options, and network conditions; no comparative benchmark is established here. Cache only when a repeated capture is acceptable for the use case. A cached clean visual is not a fresh test of a newly presented consent interface.
Reliability
- Use explicit timeouts for navigation and state waits, and report which phase failed.
- Prefer role-based or stable test selectors over brittle layout selectors.
- Use a fresh context for first-visit behavior; persistent contexts can carry consent state forward.
- For post-choice tests, verify a saved state or confirmation in addition to banner disappearance.
- For hosted services, test representative sites and recheck provider defaults when changing options or API versions.
Cost
Self-hosted browser automation has no per-shot API price in this workflow, but it consumes your compute and maintenance time for browser installation, concurrency, retries, and storage. Hosted API costs depend on each provider’s pricing and billing rules; read those terms rather than assuming all attempted captures are billed equally. ScreenshotNeo states that only clean shots are billed: bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with the response identifying verdict and billing. Its plans are Free: 1,000 shots/month; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. See ScreenshotNeo for product details.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Banner is missing in a supposed first-visit capture | The context reused cookies or local storage, the site does not show the banner in that region, or the banner loaded later. | Create a fresh context, check the target’s region and setup, and wait for the expected banner selector. Distinguish “not shown” from “choice recorded.” |
| Wait for banner times out | The selector or accessible name changed, a different consent manager is active, or navigation failed. | Inspect the page and current locator, check navigation errors, and wait for a site-specific visible condition. Avoid silently skipping the assertion. |
| Banner appears after the screenshot | The overlay is asynchronous or waits for another script or delay. | Wait for the banner or a well-defined page-ready condition before capturing. Do not rely on a short fixed sleep. |
| Banner disappears after clicking but preferences are not saved | The test checked only visual disappearance; the site may require a confirmation or another save action. | Verify the actual preference state or saved confirmation, and use a fresh context for the next independent test. |
| CSS style does not hide the banner | The selector is wrong, the UI uses Shadow DOM or a frame, or the banner is rendered under a different element. | Inspect the DOM and use Playwright’s screenshot styling support for Shadow DOM/inner frames where applicable, or target the correct frame or element. Keep the capture labeled as visually modified. |
| Overlay suppression behaves differently across APIs | Parameter names, defaults, and implementation vary by vendor and can change. | Consult the current provider reference, explicitly set the desired option, and test sample pages. Do not assume “block” means a real consent interaction. |
| Network-idle wait never completes | The page maintains polling, streaming, or analytics requests. | Wait for a specific selector or use a navigation condition appropriate to the page, then capture when the relevant state is stable. |
| Screenshot is blank or navigation fails | The target may be unavailable, blocked, slow, or dependent on scripts not yet ready. | Inspect navigation status and page errors, increase a phase-specific timeout where justified, and retry only transient failures. Do not interpret a blank image as a successful clean capture. |
FAQ
Should an API accept the banner or hide it?
Choose based on the artifact’s purpose: preserve it for first-visit evidence, interact with it for a choice-state test, or suppress it for a labeled clean visual.
Does removing the banner mean the visitor consented?
No. Removing or hiding interface pixels does not establish that a person made a choice or that processing is authorized.
Can one screenshot prove consent compliance?
No. It can document a visible state at capture time. Consent and lawful processing depend on the site’s behavior and applicable legal context.
Are banner-blocking defaults the same across providers?
No. Check each provider’s current documentation and set the intended behavior explicitly.


