How to Capture a Website Screenshot After Accepting Cookies in a Fresh Browser Context
Use Playwright to accept a cookie prompt in a new browser context, verify consent took effect, and capture a viewport or full-page screenshot.
To capture a website after accepting its cookie prompt, create a new Playwright browser context, navigate to the site, click the site’s consent control, verify the prompt has closed or another expected post-consent condition is true, then save the screenshot. A new non-persistent context gives you a clean session without reusing another context’s cookies or local storage.
The consent button and the way consent is stored vary by site, language, region, and consent platform. The example below uses a placeholder button name; inspect the target site’s actual prompt and replace the locator and verification condition.
1. Set up Playwright
This runnable JavaScript example uses Chromium. It takes the target URL from the command line, waits for the consent prompt to appear, accepts it, waits for the prompt to disappear, and saves a full-page PNG.
npm install playwright
npx playwright install chromium
// screenshot-after-consent.js
const { chromium } = require('playwright');
async function main() {
const targetUrl = process.argv[2];
if (!targetUrl) {
throw new Error('Usage: node screenshot-after-consent.js https://example.com');
}
const browser = await chromium.launch({ headless: true });
try {
// A new, non-persistent context starts with isolated browser state.
const context = await browser.newContext({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
try {
const page = await context.newPage();
await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
// Replace this with a locator matching the site's actual consent UI.
const acceptButton = page.getByRole('button', {
name: /accept all cookies|accept cookies|allow all/i,
});
await acceptButton.waitFor({ state: 'visible', timeout: 10000 });
await acceptButton.click();
// Verify the banner has closed before capturing. Adjust the locator if
// the site leaves the banner visible after accepting.
await acceptButton.waitFor({ state: 'hidden', timeout: 10000 });
await page.screenshot({ path: 'after-consent.png', fullPage: true });
await context.close();
} finally {
// Context close is safe even if navigation, clicking, or capture fails.
await context.close().catch(() => {});
}
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run it with node screenshot-after-consent.js https://example.com. The output is after-consent.png in the current directory. In production code, use a site-specific consent locator and a verification condition that reflects the site’s behavior.
2. Find and verify the site’s consent control
Use Playwright’s accessible locators where possible, since role and accessible name are easier to understand than generated CSS classes. Check the visible button text, role, and surrounding dialog in the browser or with Playwright inspection tools. Button text may be localized, so match the target locale deliberately.
Do not treat a successful click as proof that consent was recorded. Verify that the prompt disappears or that a site-specific post-consent signal appears. Some sites update asynchronously; the prompt can close before the content behind it finishes updating. In that case, wait for the relevant content or state as well.
When the prompt is inside an iframe
Some consent managers render their controls in an iframe. A locator on the main page will not find those controls. Identify the relevant frame and use a frame locator, for example:
const consentFrame = page.frameLocator('iframe[title="Privacy preferences"]');
await consentFrame.getByRole('button', { name: /accept all/i }).click();
The iframe selector and button name above are examples, not universal selectors. Inspect the target page to determine its actual frame and accessible names. If the consent UI is rendered in shadow DOM, prefer Playwright locators that can pierce open shadow roots; closed shadow roots may require a different site-supported interaction.
3. Choose the right screenshot
Playwright’s page.screenshot() captures the current page. A viewport screenshot records only what is visible in the browser viewport; fullPage: true extends the capture to the full page height.
// Visible viewport only
await page.screenshot({ path: 'viewport.png' });
// Full page
await page.screenshot({ path: 'full-page.png', fullPage: true });
// JPEG with explicit quality
await page.screenshot({ path: 'page.jpg', type: 'jpeg', quality: 85, fullPage: true });
Keep viewport dimensions, device scale factor, browser engine, locale, and timing consistent when creating repeatable screenshots or visual comparisons. Use a fixed viewport for stable layout. Full-page capture can trigger additional page work or produce a very tall image on long pages, so use it only when the whole document is needed.
4. Reuse consent state when the workflow requires it
A fresh context is useful for testing the consent flow from the beginning. If you instead need to accept consent once and restore that state in a later context, save and load Playwright storage state. The relevant state depends on the site: consent may be stored in cookies, local storage, or another mechanism. Playwright can also include IndexedDB in a storage snapshot when requested.
// After accepting consent in `context`:
await context.storageState({ path: 'consented-state.json' });
// In a later run, initialize a new context with the saved state:
const restoredContext = await browser.newContext({
storageState: 'consented-state.json',
});
const restoredPage = await restoredContext.newPage();
await restoredPage.goto(targetUrl);
await restoredPage.screenshot({ path: 'restored-consent.png', fullPage: true });
Only restore state when the goal is to reproduce a previously consented session. For a fresh-consent test, do not supply a storage-state file. Treat saved browser state as sensitive: it can contain session cookies or other credentials, so keep it out of public repositories and shared artifacts.
5. Python and cURL alternatives
Playwright’s Python API follows the same sequence: launch a browser, create a new context, navigate, interact with the site’s prompt, verify the result, and capture. Install the package and browser first:
pip install playwright
playwright install chromium
# screenshot_after_consent.py
import asyncio
import sys
from playwright.async_api import async_playwright
async def main():
if len(sys.argv) < 2:
raise SystemExit("Usage: python screenshot_after_consent.py https://example.com")
target_url = sys.argv[1]
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
try:
context = await browser.new_context(
viewport={"width": 1440, "height": 1000},
device_scale_factor=1,
)
try:
page = await context.new_page()
await page.goto(target_url, wait_until="domcontentloaded", timeout=30000)
accept_button = page.get_by_role(
"button",
name=__import__("re").compile(
r"accept all cookies|accept cookies|allow all", __import__("re").I
),
)
await accept_button.wait_for(state="visible", timeout=10000)
await accept_button.click()
await accept_button.wait_for(state="hidden", timeout=10000)
await page.screenshot(path="after-consent.png", full_page=True)
finally:
await context.close()
finally:
await browser.close()
asyncio.run(main())
Run it with python screenshot_after_consent.py https://example.com. Replace the regular expression with the prompt’s actual accessible button name. For cURL, a normal HTTP request cannot run the browser interaction needed to accept a JavaScript consent dialog. Use a browser automation tool such as Playwright, or use the ScreenshotNeo API shown below.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Timeout waiting for the button | The button text differs, the banner did not appear, or it is inside a frame. | Inspect the page and update the role/name locator. Check frames and the target locale. If the site conditionally shows consent, verify whether the prompt actually appeared. |
| Strict mode says multiple elements matched | The locator matches more than one button, such as separate desktop and mobile controls. | Narrow the locator to the dialog or consent frame, or use a more specific accessible name. Avoid selecting the first match without checking which control it is. |
| Click succeeds but banner remains | The matched control may be a preferences option, a click may be intercepted, or the banner may update asynchronously. | Confirm the chosen action, wait for the banner to hide, and inspect any resulting confirmation dialog. Use the actual accept-all control if that is the intended consent action. |
| Screenshot still shows the banner | Capture happened before the dismissal transition completed, or the locator verified the wrong element. | Wait for the banner container to become hidden, then wait for any relevant post-consent content before capture. |
| Page is blank or incomplete | Navigation reached DOM content before client-side rendering finished, or the site is slow or failed to load. | Wait for a site-specific element or a meaningful ready condition. Use a bounded timeout and report a navigation failure instead of capturing a misleading blank image. |
| Consent appears again on every run | Each new context is intentionally isolated and does not reuse the prior context’s state. | For a fresh-flow test, this is expected. To reproduce a prior accepted state, save and restore storage state, after checking which storage mechanism the site uses. |
| Output differs between runs | Viewport, locale, device scale, network timing, dynamic content, or browser engine changed. | Pin these inputs and wait for the same page condition each run. Dynamic ads and content can still vary. |
7. Performance, reliability, and cost
Browser startup and page loading usually dominate the work. If capturing multiple URLs, keep a browser process open and create a separate fresh context for each independent clean-session capture. Close each page and context when finished to release resources. Reuse a context only when sharing consent state is intentional.
Set timeouts for navigation, consent appearance, and post-click verification. Prefer waiting for a specific page condition over a fixed sleep, which can waste time on fast loads and still be too short on slow ones. Use retries selectively for transient navigation failures; do not blindly retry a consent click, since repeating an interaction may not be safe or useful. Save enough diagnostic information to identify which step failed.
Running Playwright yourself has no per-screenshot API charge, but you operate the browser runtime and its compute environment. The actual cost depends on where and how the browser runs; this guide has no benchmark or fixed cost estimate for it.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Make one GET request with the target URL to receive an image or PDF. Its clean-capture steps can accept cookie and consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Only clean shots are billed: bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides screenshot, page-info, and PDF tools for AI agents.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for request options and response details. One thousand screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots. Sign up for free and get 1,000 screenshots a month with no card.
FAQ
Does a new Playwright context delete consent state from my regular browser?
No. A new non-persistent context is isolated from other contexts and does not write its browsing data to disk.
Can I know the exact consent selector without the target URL?
No. The control depends on the site’s consent manager, language, region, and page structure. Inspect the target page and use its actual accessible control.
Should I click “accept all” or change preferences?
That depends on the test or capture requirement. Choose the control that matches the intended user action and document it so repeated captures have a clear meaning.


