To block cookie banners in a screenshot, set block.cookie_banners: true; add block.ads, block.trackers and block.chat_widgets for a fully clean page. These four switches handle most sites. For the banners that survive, this guide gives a fallback in three steps (click, remove, block the script), a table of common consent managers, and a script that renders every URL with and without blocking so you can check the result yourself. It is part of the guides; the option reference is blocking.

Tested on October 15, 2026: the variant script, request bodies and SDK examples (Node.js 22.18.0, Python 3.14.8, Ruby 4.0.7, PHP 8.5, Go 1.27) against a local mock of the RenderScreenshot API. We have not yet published renders or success rates from a consent-heavy test set; the "Check it on your URLs" section shows how to produce them.

The minimal request

GET, with the two blocking switches that have query parameters:

curl "https://api.renderscreenshot.com/v1/screenshot?url=https%3A%2F%2Fexample.com&block_cookies=true&block_ads=true" \
  -H "Authorization: Bearer $RS_API_KEY" --output clean.png

POST, with every switch:

curl -X POST https://api.renderscreenshot.com/v1/screenshot \
  -H "Authorization: Bearer $RS_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "block": { "cookie_banners": true, "ads": true, "trackers": true, "chat_widgets": true }
  }' --output clean.png

What each option does

Option Default What it does Side effects
block.cookie_banners false Clicks the first accept button it recognizes and waits 300 ms; if it finds none, hides common banner elements The page usually behaves as if consent was given
block.ads false Blocks requests to known ad networks and hides common ad containers Site-specific ad wrappers can keep their size
block.trackers false Blocks analytics and tracking scripts Most analytics tags do not fire during the capture
block.chat_widgets false Blocks Intercom, Drift, Zendesk, Crisp and similar None known
block.requests none Blocks URLs matching glob patterns (*, **, ?) Blocking a script the page needs can break rendering
block.resources none Blocks image, font, media, script or stylesheet script and stylesheet often break layout

Combine cookie_banners with trackers. Because the banner is usually dismissed by accepting, the site may then load the analytics and marketing tags it was waiting to load. block.trackers stops most of them.

When a banner survives: a three-step fallback

Consent managers change their markup, and some sites build their own banner. Work through these steps in order and stop at the first that works.

1. Click the site's own accept button. This keeps the page in the state a real visitor sees.

{
  "url": "https://example.com",
  "block": { "cookie_banners": true },
  "page": { "click": "#onetrust-accept-btn-handler", "click_required": false }
}

click_required: false (the default) means the capture still succeeds on pages where the button is absent. The renderer waits 300 ms after the click. If the banner fades out more slowly, add an inline script such as new Promise((r) => setTimeout(r, 600)): scripts run after the click and the renderer waits for the promise. wait.delay does not help here, because it runs before the click.

2. Remove the banner container. When clicking does nothing, or the site reloads after consent, remove the container and the dimmed backdrop:

{
  "url": "https://example.com",
  "page": { "remove": ["#onetrust-consent-sdk", "#CybotCookiebotDialog", ".cookie-overlay"] }
}

page.remove collapses the space; page.hide keeps it, which matters for banners that sit in the page flow rather than on top of it.

3. Block the consent manager's script. No script, no banner:

{
  "url": "https://example.com",
  "block": { "requests": ["*cdn.cookielaw.org*", "*consent.cookiebot.com*"] }
}

Use this last. Some sites hold back content until consent arrives, so with the script blocked you may get placeholders where videos, maps or even prices should be.

These selectors and hosts are the ones in common use in October 2026. Vendors change markup, so confirm them in your browser's inspector before you rely on them.

Consent manager Container to remove Accept button to click Script host to block
OneTrust #onetrust-consent-sdk #onetrust-accept-btn-handler cdn.cookielaw.org
Cookiebot #CybotCookiebotDialog #CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll consent.cookiebot.com
Didomi #didomi-host #didomi-notice-agree-button sdk.privacy-center.org
TrustArc #truste-consent-track #truste-consent-button consent.trustarc.com
Usercentrics #usercentrics-root (shadow DOM) inside the shadow root app.usercentrics.eu

Usercentrics renders its banner inside a shadow root, which page selectors cannot reach into. Remove the host element, #usercentrics-root, or block the script host.

Banners that appear late

The built-in clean-up runs once, after the page loads and after wait.delay, and before your page changes. A banner that a site shows two or three seconds after load is not there yet when the clean-up runs. Set wait.delay (up to 10,000 ms) so the banner exists by then.

Ads: collapse the empty slots

block.ads stops ad network requests and also hides common ad containers, such as AdSense ins.adsbygoogle elements and Google ad iframes. Sites often wrap those in their own containers with a reserved height, to avoid layout shift. The result is a blank rectangle. Remove the site's wrappers:

{
  "url": "https://example.com/article",
  "block": { "ads": true },
  "page": { "remove": ["ins.adsbygoogle", ".ad-slot", "[id^='div-gpt-ad']"] }
}

Use the class or ID the site uses for its own ad wrappers; the three above are examples, not a complete list.

SDK examples

All four switches plus a URL pattern, in each official SDK. page.click and page.remove have no documented builder method; send them in the POST body.

Node.js

import { Client, TakeOptions } from 'renderscreenshot';

const client = new Client(process.env.RS_API_KEY);
const image = await client.take(
  TakeOptions.url('https://example.com').blockAds().blockTrackers()
    .blockCookieBanners().blockChatWidgets().blockUrls(['*cdn.cookielaw.org*'])
);

Python

image = client.take(
    TakeOptions.url('https://example.com').block_ads().block_trackers()
    .block_cookie_banners().block_chat_widgets().block_urls(['*cdn.cookielaw.org*'])
)

Ruby

image = client.take(
  RenderScreenshot::TakeOptions.url('https://example.com').block_ads.block_trackers
    .block_cookie_banners.block_chat_widgets.block_urls(['*cdn.cookielaw.org*'])
)

PHP

$image = $client->take(
    TakeOptions::url('https://example.com')->blockAds()->blockTrackers()
        ->blockCookieBanners()->blockChatWidgets()->blockUrls(['*cdn.cookielaw.org*'])
);

Java

byte[] image = client.take(
    TakeOptions.url("https://example.com").blockAds().blockTrackers()
        .blockCookieBanners().blockChatWidgets().blockUrls(Arrays.asList("*cdn.cookielaw.org*"))
);

Go

image, err := client.Take(ctx, rs.URL("https://example.com").BlockAds().BlockTrackers().
    BlockCookieBanners().BlockChatWidgets().BlockURLs([]string{"*cdn.cookielaw.org*"}))

The Java example follows the Java SDK reference and was not run; the others ran against the mock API.

Check it on your URLs

Blocking is heuristic, so measure it on the pages you care about. This script renders each URL three ways (no blocking, banners only, everything) and saves the images next to each other. It costs 3 credits per URL.

// variants.mjs: render each URL with and without blocking, so you can compare the images side by side.
// Node 22+. Usage: RS_API_KEY=rs_live_... node variants.mjs urls.txt   (writes ./out/*.png)
import { mkdir, readFile, writeFile } from 'node:fs/promises';

const API = process.env.RS_API_BASE ?? 'https://api.renderscreenshot.com';
const VARIANTS = {
  none: {},
  banners: { block: { cookie_banners: true } },
  clean: { block: { cookie_banners: true, ads: true, trackers: true, chat_widgets: true } },
};

await mkdir('out', { recursive: true });
const urls = (await readFile(process.argv[2] ?? 'urls.txt', 'utf8')).split('\n').filter(Boolean);
for (const url of urls) {
  for (const [name, extra] of Object.entries(VARIANTS)) {
    const res = await fetch(`${API}/v1/screenshot`, {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.RS_API_KEY}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ url, viewport: { width: 1280, height: 800 }, ...extra }),
    });
    if (!res.ok) { console.error(`${url} [${name}]: ${res.status} ${await res.text()}`); continue; }
    const file = `out/${new URL(url).hostname}-${name}.png`;
    await writeFile(file, Buffer.from(await res.arrayBuffer()));
    console.log(`${file}`);
  }
}

Open the out/ folder and look at each trio. Record, per URL: banner gone (yes or no), ad slots collapsed, chat bubble gone, and any content missing in the clean image that is present in none. Missing content almost always means a site that gates content on consent; for those, use the click step instead of blocking. Failed renders are printed with their typed error and cost no credits.

Troubleshooting

Symptom Likely cause Fix
Banner still visible Custom or new consent manager markup, or the banner appeared after the clean-up ran Fallback step 1 or 2 with the live selector; wait.delay for late banners
Dark overlay remains after the banner is gone Backdrop is a separate element Add the backdrop selector to page.remove
Embedded videos or maps show placeholders Consent script blocked; the site waits for consent Click accept (step 1) instead of blocking the script
Blank rectangles where ads were The site's own wrapper reserves the height page.remove the wrapper
Chat bubble still in the corner Widget not on the built-in list block.requests with the widget's host, or page.hide its container
Page layout broken block.resources includes script or stylesheet Block specific URLs with block.requests instead
Banner removed but the page looks unstyled or empty Removed a wrapper that contains the page Use a narrower selector; check it in the inspector
Click step fails the capture click_required: true on a page without that button Leave click_required false for mixed URL lists

Related in this topic

Was this page helpful?