# Clean screenshots

> Remove cookie banners, ads, trackers and chat widgets, hide elements, inject CSS and JavaScript, click and wait.

## Remove the clutter

Turn on the blockers you need:

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","block_cookie_banners":true,"block_ads":true,"block_trackers":true,"block_chats":true}' \
  --fail-with-body -o shot.jpg
```

| Option | What it does |
| --- | --- |
| `block_cookie_banners` | Blocks consent-manager scripts (OneTrust, Cookiebot, Didomi, Usercentrics, …), hides their elements, and re-enables scrolling they lock. |
| `block_banners_by_heuristics` | Also removes fixed or sticky overlays that look like banners, modals or newsletter popups. Aggressive, so check the result. |
| `block_ads` | Blocks requests to ad networks. |
| `block_trackers` | Blocks analytics and tracking scripts. Pages often load faster too. |
| `block_chats` | Blocks and hides chat widgets (Intercom, Drift, Crisp, HubSpot, …). |

Blocked requests never load, so nothing flashes on screen and nothing is left to hide.

### Block anything else

`block_requests` takes wildcard patterns matched against request URLs, and `block_resources` blocks whole resource types:

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","block_requests":["*.doubleclick.net/*","*/analytics.js"],"block_resources":["media","font"]}' \
  --fail-with-body -o shot.jpg
```

Resource types: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest`, `other`. The page itself is never blocked.

## Hide or restyle elements

`hide_selectors` hides every element matching any selector. `styles` injects any CSS:

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","hide_selectors":[".newsletter-popup","#promo-bar"],"styles":"header { position: static !important } body { font-size: 18px }"}' \
  --fail-with-body -o shot.jpg
```

## Run JavaScript

`scripts` runs in the page after it loads, before the capture. Use it to open sections, fill in demo data or remove things CSS can't reach:

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","scripts":"document.querySelectorAll('\''details'\'').forEach(d => d.open = true)"}' \
  --fail-with-body -o shot.jpg
```

If the script throws, the request fails with `script_triggers_error`. If it navigates (submitting a form, following a link), set `scripts_wait_until` so the capture waits for the next page. On sites with a strict Content-Security-Policy, scripts that load other scripts may need `bypass_csp=true`.

## Click and hover

`click` and `hover` act on the first visible element matching a selector, after scripts:

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","click":"button.load-more","delay":1}' \
  --fail-with-body -o shot.jpg
```

By default a missing element fails the request with `selector_not_found`. Set `error_on_click_selector_not_found=false` (or the `hover` equivalent) for elements that are only sometimes there, like a promo popup.

## Wait for the right moment

By default the capture happens after the `load` event, once the network has been briefly idle, the page has stopped changing, and fonts and visible images have loaded (each capped at a few seconds). For pages that render even later:

| Option | Waits for |
| --- | --- |
| `wait_until` | `load`, `domcontentloaded`, `networkidle0` (no requests for 500 ms) or `networkidle2` (at most 2). |
| `wait_for_selector` | An element to appear, such as a chart rendered by JavaScript. |
| `delay` | A fixed number of seconds, for animations. |

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","wait_until":["networkidle0"],"wait_for_selector":".chart canvas"}' \
  --fail-with-body -o shot.jpg
```

Prefer `wait_for_selector` over `delay`: it's as fast as the page allows and fails clearly with `selector_not_found` if the content never shows up.

## Freeze animations

`reduce_motion=true` finishes CSS animations and transitions and pauses videos, so hero sections aren't caught half-faded. `dark_mode=true` renders sites that support it in dark mode.

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","reduce_motion":true,"dark_mode":true}' \
  --fail-with-body -o shot.jpg
```

## Fail instead of capturing a broken page

When screenshots feed something automated, a picture of an error page is worse than an error. These options fail the request instead:

```bash
curl -X POST "https://shotkit.net/api/take" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/product/42","fail_if_content_missing":["Add to cart"],"fail_if_content_contains":["Access denied"],"fail_if_request_failed":["*api.example.com*"]}' \
  --fail-with-body -o shot.jpg
```

A 4xx or 5xx from the page itself already fails with `host_returned_error` unless `ignore_host_errors=true`. Failed requests don't count toward your monthly screenshots.
