Introduction
You've probably shipped some form of visual automation already - monitoring, QA diffs, documentation screenshots, "URL to image" previews, report generation, et cetera.
The part that keeps getting worse isn't rendering, it's the stuff layered on top: cookie banners, consent modals, region-specific cookie warnings, and cookie walls that block the whole page until a user clicks something.
If you're trying to capture reliable screenshots, you end up doing "cookie bypass" work whether you call it that or not. The goal is getting a clean, representative screenshot of the page your users actually came for - without a consent overlay partially blocking every capture.
In this guide, you'll learn:
- Why cookie banners break automated screenshots in ways that are hard to fix later.
- How a screenshot API helps.
- Practical patterns for handling accept cookies, reject cookies, and decline cookies flows.
- How to scale this without leaking sensitive data into user logs or building a brittle headless stack.
What is cookie bypass?
In the context of screenshot APIs, cookie bypass is simple: you're making your screenshot pipeline resilient to cookie consent UI so your captures work properly and aren't obscured.
Sometimes that means accepting cookies because your internal monitoring wants the "default" experience your marketing team expects. Sometimes it means rejecting cookies because you don't want advertising cookies, or you're validating consent UX. Sometimes it means continuing without accepting and capturing the page underneath.
The tricky part is that the banner itself is a product of browser state - browser cookies, site data, localStorage, and geolocation rules. If your screenshot tool doesn't manage that state, you're stuck with random overlays and flaky results.
Cookie consent is also regulated. In the EU, the ePrivacy Directive governs storing or gaining access to information stored on a user's device, and it's deeply connected to consent flows and how cookie consent is presented.
The problem with cookie banners
Cookie banners break screenshots for a few common reasons:
- They're usually fixed-position overlays with a high z-index, so your screenshot is correct, but useless.
- They can block scroll, trap focus, and intercept user clicks, so the page never reaches the layout you want to capture.
- They're conditional on the prior consent state, so the same URL produces different output depending on cookie values and locally stored site data.
If your pipeline is based on alternatives to headless Chrome screenshot tools - for example, basic HTML-to-image renderers or simple fetch-and-rasterize services - you'll often miss the real browser behavior entirely.
Modern consent flows are app-like. They can depend on client-side frameworks, dynamic content, and third-party scripts.
There's also a UX reality: many banners aren't just a banner. Cookie walls force users to choose before they gain access to content. Some implementations make rejecting cookies harder than accepting cookies, which regulators have called out explicitly. The European Data Protection Board (EDPB) has documented patterns like "no reject button on the first layer," which matters if your automation expects a straightforward reject/accept choice.
You can't bypass cookie banners with a naive screenshot flow
A naive flow is usually:
- POST a URL to a service.
- Immediately screenshot the viewport.
- Save the binary response.
That fails because the banner is a runtime UI. Even if you wait for DOMContentLoaded, the cookie consent UI often loads later (tag managers, consent platforms, A/B tests). If you capture too early, you might miss the banner - and then your next run captures it - which is worse than consistently capturing it.
If you try to fix this by persisting cookies, you introduce another problem: cookies are sensitive data. Session cookies can act like bearer tokens in most cases. Logging them into user logs, or leaving them stored locally on a build agent, can turn into an incident.
That's not hypothetical: Cookie theft and stolen cookies are widely used in session hijacking ("pass-the-cookie") because they can let an attacker stay signed in without re-authentication prompts, even with multifactor authentication (MFA) enabled. Some attacks specifically target Microsoft cookies like ESTSAUTHPERSISTENT.
So yes, you can extract cookies - but treat cookie values like secrets, and don't normalize storing them in logs.
The solution: a screenshot API
A screenshot API gives you a real browser on the server side and returns an image, or sometimes a PDF export via a related endpoint. Instead of wiring up your own headless fleet, you make one HTTPS request from whatever system you already run.
Browserless's /screenshot endpoint is a good example:
- Method: POST.
- Path: /screenshot.
- Auth: token query parameter (?token=).
- Content-Type: application/json.
- Response: image/png (or image/jpeg, image/webp based on options.type).
The key difference compared to homegrown scripts is that a production-grade API can standardize the hard parts, which include: browser versions, fonts, sandboxing, concurrency, retries, and consistent server behavior across environments.
What is a screenshot API?
A screenshot API is a web service that takes a URL or HTML, opens it in a browser on a server, renders it, and returns a screenshot as a binary response.
You can think of it as a URL-to-image API with JavaScript examples baked in. Instead of running Google Chrome on your own server and calling page.screenshot(), you call an endpoint like /screenshot and save the response.
Where this becomes useful for cookie bypass is that you can pair rendering with behavior:
- Wait for the UI you care about.
- Hide or dismiss overlays.
- Block known banner scripts or third-party resources.
- Capture full-page output consistently.
How screenshot APIs solve the cookie banner issue
Cookie banners are a browser problem, not a network problem. A screenshot API solves that by giving you:
- A consistent browser environment per request
- Controls to wait for selectors, timeouts, events, or functions before capturing, such as waitForEvent
- Hooks to inject scripts/styles pre-screenshot, which are useful for dismissing or neutralizing overlays
- Options to block resource types or URL patterns so you can skip the worst offenders (ads, trackers, sometimes the consent platform itself) by using rejectResourceTypes and rejectRequestPattern
That's your cookie bypass toolbox. You're not breaking consent, you're deciding how your automation should behave when cookie consent is presented.
Here's a practical mental model:
- If the banner is cosmetic - hide it, or screenshot an element below it by using the selector option.
- If the banner gates content - pick a consent path (accept or reject) so the page can load.
- If the banner is region-driven - try using geolocated residential proxies located in a different country.
What's the best screenshot API for web automation?
When you're assessing screenshot APIs for web automation, look for these capabilities:
- Real browser controls - Waiting (selectors/timeouts/functions), navigation tuning, and predictable rendering.
- Full-page support - If you need long-scroll captures, not just viewports.
- Request-level performance controls - Block heavy resources with rejectResourceTypes and rejectRequestPattern.
- A path for bot detection - Many sites will treat screenshot automation as a bot. A screenshot API with stealth and anti-bot options matters when you're capturing behind modern defenses.
- Security hygiene - You want strong defaults around tokens, and you want to avoid dumping cookies into logs.
Browserless is a strong fit when your requirements go beyond just grabbing a PNG. The /screenshot endpoint covers the straightforward cases. When bot detection blocks screenshots, Browserless explicitly recommends switching to BrowserQL (BQL), our stealth-first automation API.
You also get adjacent endpoints that matter in real systems:
- A screenshot api with PDF export via the /pdf endpoint when the output needs to be a document, not an image.
- Session-aware automation when you need state to persist - useful when consent is stored locally and you don't want to click it every run.
How to use a screenshot API to bypass cookie banners
The code below is the fastest way to get a baseline screenshot working with Browserless. From there, cookie bypass is usually a small extension of the same request: add waits, inject a small script to click a consent button, or block the banner resources.
The Browserless /screenshot endpoint is a POST request that returns the image bytes. You supply url and an options object (Puppeteer-style).
A pragmatic cookie banner playbook looks like this:
- Wait for the banner - Use waitForSelector so your script doesn't race the UI.
- Choose a policy - Accept cookies, reject cookies, or decline cookies based on your use case by injecting js scripts through the addScriptTag option.
- Dismiss or hide - Inject a short script or CSS rule with addStyleTag before capture, especially when the banner blocks nothing but the screenshot.
Browserless offers a blockConsentModals option that automatically removes known cookie banners and consent overlays before capturing, so you don't need to write custom dismissal scripts. Pass "blockConsentModals": true in your /screenshot request body and the most common CMPs are handled out of the box.
Below are the quickstart snippets you can use with Browserless, organized by language.
C#
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
string TOKEN = "YOUR_API_TOKEN_HERE";
string url = $"https://production-sfo.browserless.io/screenshot?token={TOKEN}";
string jsonData = @"
{
""url"": ""https://example.com/"",
""options"": {
""fullPage"": true,
""type"": ""png""
}
}";
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = new StringContent(jsonData, Encoding.UTF8, "application/json")
};
request.Headers.Add("Cache-Control", "no-cache");
try
{
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var imageBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("screenshot.png", imageBytes);
Console.WriteLine("Screenshot saved as screenshot.png");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
This is the "baseline" request: one POST, one binary response, one file written. Your cookie bypass work happens by extending the JSON payload (waits, scripts, resource blocking), not by changing the overall flow.
cURL
curl -X POST \
"https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE" \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/",
"options": {
"fullPage": true,
"type": "png"
}
}' \
--output "screenshot.png"
If you're debugging cookie warnings, cURL is still the quickest way to iterate. You can replay the exact same request your server will send, then inspect how the target site responds.
Java
import java.io.*;
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class TakeScreenshot {
public static void main(String[] args) {
String TOKEN = "YOUR_API_TOKEN_HERE";
String url = "https://production-sfo.browserless.io/screenshot?token=" + TOKEN;
String jsonData = """
{
"url": "https://example.com/",
"options": {
"fullPage": true,
"type": "png"
}
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Cache-Control", "no-cache")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonData))
.build();
try {
HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
Files.copy(response.body(), Paths.get("screenshot.png"), StandardCopyOption.REPLACE_EXISTING);
System.out.println("Screenshot saved as screenshot.png");
} catch (Exception e) {
e.printStackTrace();
}
}
}
For automation pipelines on the JVM, the important detail is you're not juggling headless browser processes. Your service makes an HTTPS request, gets bytes back, and stores them.
Javascript
import fs from "fs/promises";
const TOKEN = "YOUR_API_TOKEN_HERE";
const url = `https://production-sfo.browserless.io/screenshot?token=${TOKEN}`;
const headers = {
"Cache-Control": "no-cache",
"Content-Type": "application/json",
};
const data = {
url: "https://example.com/",
options: {
fullPage: true,
type: "png",
},
};
const takeScreenshot = async () => {
const response = await fetch(url, {
method: "POST",
headers: headers,
body: JSON.stringify(data),
});
const imageBuffer = await response.arrayBuffer();
await fs.writeFile("screenshot.png", Buffer.from(imageBuffer));
console.log("Screenshot saved as screenshot.png");
};
takeScreenshot();
This is a clean URL-to-image API with JavaScript examples and style integration - call, buffer, and write. From here, you can add wait conditions so your screenshot happens after you've handled the cookie consent UI.
Python
import requests
TOKEN = "YOUR_API_TOKEN_HERE"
url = f"https://production-sfo.browserless.io/screenshot?token={TOKEN}"
headers = {
"Cache-Control": "no-cache",
"Content-Type": "application/json"
}
data = {
"url": "https://example.com/",
"options": {
"fullPage": True,
"type": "png"
}
}
response = requests.post(url, headers=headers, json=data)
with open("screenshot.png", "wb") as file:
file.write(response.content)
print("Screenshot saved as screenshot.png")
Python is often where teams start, and it's also where brittle headless stacks show up first, usually in CI crashes, font issues, or sandbox flags. The API approach keeps your code small and your failure modes mostly in one place.
Conclusion
Cookie banners aren't going away, and just cropping the overlay out isn't a strategy. A screenshot API gives you a real browser with practical controls, so you can decide how your automation handles cookie consent: accept, reject, decline, or hide - depending on what you're validating.
Start with the baseline /screenshot request, then add the minimal behavior needed to make the page render the state you actually want to capture, with waits, small injections, and resource blocking. If bot detection becomes the limiting factor, move up to a stealth-first path like BrowserQL rather than trying to duct-tape a headless Chrome setup in your own infrastructure.
And keep the security line clear: cookie values and session state can equal access. Don't let your screenshot pipeline become a cautionary tale because someone dumped auth cookies into logs or artifacts.
Screenshot API FAQs
How do I handle cookie banners in automated screenshots?
Browserless offers a blockConsentModals option that automatically removes known cookie banners and consent overlays before capturing, so you don't need to write custom dismissal scripts. Pass "blockConsentModals": true in your /screenshot request body and the most common CMPs are handled out of the box.
Treat this process as a deterministic UI step, not a random annoyance. Pick a policy - accept cookies vs. reject cookies - that matches your product need, then make it explicit in your automation.
In Browserless REST APIs, the shared request configuration options let you wait for selectors/timeouts/functions, and the screenshot endpoint supports injecting scripts/styles before capture. That combination is usually enough to dismiss cookie banners reliably without human user clicks.
If a site turns consent into a hard gate with cookie walls, don't fight the browser. Either choose a consent branch you're allowed to take, or treat that as a case where you cannot capture what you want without an interaction, and then move it into a session or hybrid workflow.
Which API should I use for full-page screenshots?
Use an API that supports full-page capture as a first-class option. In Browserless /screenshot, that's options.fullPage: true, which gives you a long capture instead of just the viewport.
If your actual requirement is a document, not an image, use a screenshot API with PDF export via a dedicated PDF endpoint. Browserless provides /pdf as a sibling REST API, returning application/pdf.
How do I take screenshots at scale?
At scale, screenshotting is mostly systems work:
- Concurrency control - Don't spawn infinite workers. Rate limit per target domain, and keep retries idempotent.
- Good logging - Log request IDs, timings, and high-level outcomes. Avoid logging cookie values or tokens into user logs.
- State strategy - For consent flows, either re-handle the banner each time, or use a sessions feature designed for persisting state across runs (cookies/localStorage) with explicit lifecycle management.
- Bot detection fallback - If the screenshot endpoint gets blocked, switch to a stealth-first automation path (BrowserQL) instead of stacking hacks onto a single endpoint.
That's the difference between a demo and a pipeline that keeps working six months later.