Bulk Website Screenshots: Full Page, PDF, No Cookie Banners avatar

Bulk Website Screenshots: Full Page, PDF, No Cookie Banners

Pricing

from $0.52 / 1,000 screenshots

Go to Apify Store
Bulk Website Screenshots: Full Page, PDF, No Cookie Banners

Bulk Website Screenshots: Full Page, PDF, No Cookie Banners

Screenshot a list of URLs or call it as an instant API: full-page or viewport, PNG, JPEG, WebP or PDF, desktop, tablet or mobile, dark mode. Cookie banners and ads hidden. $0.75 per 1,000 screenshots ($0.52 on Business) + usage. No screenshot fee for failed pages.

Pricing

from $0.52 / 1,000 screenshots

Rating

0.0

(0)

Developer

Open Data Tools

Open Data Tools

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

14 hours ago

Last modified

Share

This tool takes screenshots of a list of web pages in one run. Paste in your URLs and get a clean image (or PDF) of each page: the whole page from top to bottom or just the first screen, on desktop, laptop, tablet or mobile, in PNG, JPEG, WebP or PDF. Cookie pop-ups and ads are hidden automatically, and lazy-loaded images are loaded before the capture so long pages don't come out half empty. You pay $0.75 per 1,000 screenshots on the Free plan ($0.52 on Business) plus Apify platform usage. Pages that fail have no screenshot fee, though time spent counts as usage.

Who it's for

  • Agencies and freelancers capturing client sites, competitor pages or portfolio thumbnails.
  • SEO and marketing teams keeping a visual record of landing pages, ads and search pages over time.
  • Developers and QA teams checking how pages look on different devices after a release.
  • Researchers, archivists and compliance teams saving proof of what a page showed on a given day.
  • AI agents and automations that need a picture of a page.

Why use it

  • Full-page shots that work: the page is scrolled first so lazy-loaded images and sections appear, then captured in one piece. Very long pages are cut at a height you choose instead of failing, and empty space at the bottom of a page is trimmed off so the image ends where the content ends.
  • No cookie banners, ads or empty ad boxes: uses the same block lists as browser ad blockers (EasyList, EasyPrivacy and Fanboy's Cookie List) plus our own rules for the big consent providers (OneTrust, Cookiebot, Usercentrics, Quantcast, Didomi, TrustArc, Sourcepoint and more). Sign-up, newsletter, donation and "turn off your ad blocker" pop-ups are hidden too, and pages they freeze are unlocked. Nothing is clicked or accepted on your behalf.
  • No screenshot fee for failed pages: pages that don't load, return an error, show a bot check, come out blank or nearly blank, show the site's own error page (such as "Oops, unexpected error" or "Something went wrong"), show only loading placeholders (grey boxes where the content should be) or are file downloads get a row with a plain-English reason and no screenshot fee, though time spent counts as usage.
  • Slow pages still get captured: the Time limit per page (seconds) (pageTimeoutSecs, 25 seconds by default) covers loading only. If a page is still loading when it is reached, the tool stops waiting and captures what has appeared so far, with the note captured before the page finished loading. This also covers pages whose fonts or scripts never finish. Scrolling and taking the screenshot get their own time after that (about 22 seconds at the default settings, or less if loading ran long). If a full-page shot still can't be taken in time (for example on a page whose animations keep the browser busy), the first screen is captured instead, marked truncated with the note only the first screen was captured. Only if nothing at all has appeared by the limit is the page marked failed (no screenshot fee, though time spent counts as usage), with the tip to raise Time limit per page (seconds).
  • Every format and device: PNG, JPEG, WebP or A4 PDF; desktop, HD, Retina, laptop, tablet, mobile, iPhone or Android; light or dark mode; transparent backgrounds; or your own screen size.
  • Just the part you want: capture a single element with a CSS selector, wait for a specific element to appear, or hide extra things like chat widgets.
  • Fast and lean in bulk: several pages are captured at once, set automatically from the run's processor power. Video, trackers and decorative animation are switched off, so each capture uses far less computing time.
  • Stays within your budget: set a maximum cost per run and the run stops taking new screenshots once it is reached (see Staying within budget).

How to use it

  1. Add your pages under URLs to capture. Bare domains like example.com work too.
  2. Choose a Format and Device. Leave Full page on to capture whole pages.
  3. Click Start.
  4. Open the Output tab. The overview shows a thumbnail, the URL, the status and any reason a page wasn't captured. Each row has a screenshotUrl link to the file. All files are also in the run's key-value store.
  5. To capture pages regularly, create a schedule with the same input and connect an integration (Google Drive, Slack, Zapier, Make, a webhook) to send the files on.

Pricing

You pay per screenshot captured and saved, plus Apify platform usage (the computing time of your run). Failed, blocked, blank, error-page, placeholder-only and skipped pages have no screenshot fee, though the time spent trying them counts as usage.

Your Apify planPer 1,000 screenshots
Free$0.75
Starter$0.70
Scale$0.62
Business and above$0.52

Each run also has a start fee of $0.0002 at the default 4 GB memory ($0.00005 per GB). An Instant API instance pays the same each time it starts.

Platform usage depends on the pages. We measured it on build 0.1.8 with the default settings (4 GB memory, full page, desktop). The figures below are total usage, including storage and data transfer, per 1,000 screenshots captured, at the Free plan rate:

  • Typical sites (small business sites, blogs, documentation, SaaS homepages): about $1.50 of usage per 1,000, so about $2.25 in total with the $0.75 screenshot fee. In a test of 50 such sites, 48 were captured, 1 was blocked and 1 failed.
  • Hard sites (big news sites and online shops with heavy bot protection, very long pages and slow scripts): about $3.56 of usage per 1,000 captured, so about $4.31 in total. In a test of 50 such sites, 29 were captured, 16 were blocked and 5 failed. Blocked and failed pages have no screenshot fee, but the time spent on them still counts as usage, which is why this figure is higher.

Usage is lower on higher Apify plans and for first-screen-only captures.

Example on the Free plan: 500 typical pages costs $0.38 in screenshot fees plus about $0.75 of usage, about $1.13 in total.

Staying within budget

  • Batch runs: in Apify Console, set Maximum cost per run in the run options before you click Start. When starting a run through the API, pass maxTotalChargeUsd (in the Python apify-client, max_total_charge_usd). Once the limit is reached the run stops taking new screenshots, the remaining pages get skipped rows with no screenshot fee, and the run ends cleanly. A run stopped this way is not marked Failed.
  • Instant API: Apify's Standby mode currently has no query parameter or header for setting a maximum charge per request. An answer of 402 means the API has reached its spending limit for now; nothing is charged. To cap your total spending, set a usage limit on your Apify account (Billing, Limits), or use batch runs with a maximum cost per run.

Good to know

  • Pages are loaded with animations reduced and video switched off, which looks the same in a still image and makes captures faster.
  • Some sites use strong bot protection and show a check page instead of the real page. Those rows are marked blocked and have no screenshot fee, though time spent counts as usage.
  • Only public websites can be captured. Private and internal network addresses are refused.
  • Mobile, tablet, Retina, iPhone and Android captures use a higher pixel density, so images are 2 to 3 times the listed width.
  • WebP images can't be taller than 16,383 pixels, so very long WebP captures are cut at that height.
  • Full-page captures stop at Max page height (px) (15,000 px by default) and at 40 million pixels in total, so they stay quick to capture. That only matters at high pixel density: on desktop-retina, for example, the limit is about 6,900 px of page. Taller pages are cut, marked truncated and noted, for example only the top 15,000 px of the page was captured (height limit).
  • If a full page can't be captured in time, the top four screens are captured instead (a shorter page is retaken whole), or the first screen if even that isn't possible. When less than the whole page is captured, the row shows truncated: true and a note saying which. Pages that scroll inside a box instead of the browser window (common on app-style sites) are opened up so the whole page is captured; if the box can't be opened up, only the first screen is captured, with truncated: true and a note.
  • Empty space at the bottom of a full-page capture (a screen or more of the page's plain background) is trimmed off. Coloured sections such as footers are kept. A page cut at the height limit still shows truncated: true after trimming.
  • A page counts as blank (no screenshot fee) when it is all one colour, or almost all one colour with no text or only "Loading...". A page counts as an error page (no screenshot fee) when its text is mainly an error message such as "Oops", "unexpected error", "something went wrong", "page not found" or "service unavailable", with little else on it, or when the page's main content is such a message (or its main heading is, with little else under it), even with the site's normal header, menus and footer around it. Help and FAQ pages that mention an error within longer content, articles that explain an error (such as "Error 404: what it means") and restaurant menus are captured and charged as normal. Minimalist pages with real content are captured and charged as normal.
  • A page counts as loading placeholders (no screenshot fee) when most of it is flat light-grey boxes where the content should be and it has little text, usually because it was still loading at the time limit. Pages with grey sections, card grids, product pictures or minimalist designs are captured and charged as normal.
  • Files larger than 9 MB can't be saved. Very tall PNGs are saved as JPEG instead (noted in the row), or lower Max page height (px).

API and reference

This section is for developers and AI agents.

Input

FieldTypeDefaultDescription
startUrlsarrayrequiredPages to capture, as {"url": "..."} objects or plain strings. Bare domains get https:// added. Duplicates are captured once. Non-http(s) URLs get a failed row with no screenshot fee.
formatpng, jpeg, webp, pdfpngFile type. PDF is A4 with backgrounds, laid out like the screen version.
fullPagebooleantrueWhole page (after scrolling to load lazy content) or only the first screen. Ignored for PDF and element captures.
devicedesktop, desktop-hd, desktop-retina, laptop, tablet, mobile, iphone, androiddesktop1440x900, 1920x1080, 1440x900 @2x, 1280x800, 820x1180 @2x, 390x844 @2x, 393x852 @3x, 412x915 @2.625x, each with a matching browser identity.
hideCookieBannersbooleantrueHide consent pop-ups (Fanboy's Cookie List plus our own rules) and unlock scrolling. Nothing is accepted.
hidePopupsbooleantrueHide other pop-ups covering the page (sign-up, newsletter, donation, app, ad-blocker notices).
blockAdsbooleantrueBlock ads and trackers (EasyList, EasyPrivacy) and hide empty ad slots.
darkModebooleanfalseAsk the site for its dark theme (prefers-color-scheme: dark).
transparentBackgroundbooleanfalsePNG/WebP: pages without their own background come out see-through.
waitUntildomcontentloaded, load, networkidleloadHow long to wait before capturing. Pages that never reach it are captured anyway with a note.
delaySecsinteger0Extra pause before capturing (0 to 30). Visible images are always waited for.
maxPageHeightinteger15000Full-page captures are cut at this height in CSS pixels (500 to 30,000); the row shows truncated: true.
qualityinteger80JPEG and WebP quality (10 to 100).
selectorstringnoneCapture only the first element matching this CSS selector. No match gives a failed row with no screenshot fee.
waitForSelectorstringnoneWait until an element matching this selector is visible before capturing. If it never appears, the row is failed (no screenshot fee).
hideSelectorsstring[][]Extra CSS selectors to hide before capturing.
viewportWidth, viewportHeightintegerdevice sizeOverride the screen size.
pageTimeoutSecsinteger25Time limit for loading each page (15 to 180). A page still loading at the limit is captured as it is and marked captured before the page finished loading (charged only if something real is on it: a blank page or bot check has no screenshot fee). A page that sends nothing at all in time is marked failed (no screenshot fee, though time spent counts as usage) and is not tried again; raise this for very slow sites. Quick network errors, and pages the browser closed unexpectedly, are tried once more on a fresh page. Pages that show a bot check or refuse access (HTTP 401/403/429) are given up on straight away and marked blocked, also with no screenshot fee.
maxConcurrencyintegerfrom memoryPages captured at once (1 to 16). Default: 3 per 4 GB (one processor core per 4 GB on Apify).

Output

One dataset row per URL.

FieldTypeDescription
urlstringInput URL
finalUrlstringURL after redirects
statusstringok, failed, blocked or skipped. Only ok rows are charged.
reasonstringWhy a page wasn't captured
httpStatusintegerHTTP status of the page
pageTitlestringPage title
screenshotUrlstringDirect link to the file
screenshotKeystringKey of the file in the run's default key-value store
formatstringpng, jpeg, webp or pdf
devicestringDevice used
fullPagebooleanWhole page captured
width, heightintegerImage size in pixels (null for PDF)
bytesintegerFile size
truncatedbooleanPage was cut: taller than maxPageHeight (or the 40-million-pixel limit), too tall to capture in full in time, or only the first screen could be captured (see note)
notestringAnything worth knowing, e.g. captured before the page finished loading, only the first screen was captured or only the top of the page was captured (too tall to capture in time)
loadMsintegerTime to load and capture
capturedAtstringISO 8601 time

Example row:

{
"url": "https://example.com",
"finalUrl": "https://example.com/",
"status": "ok",
"reason": null,
"httpStatus": 200,
"pageTitle": "Example Domain",
"screenshotUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/example-com-desktop-1a2b3c4d5e.png",
"screenshotKey": "example-com-desktop-1a2b3c4d5e.png",
"format": "png",
"device": "desktop",
"fullPage": true,
"width": 1440,
"height": 900,
"bytes": 18734,
"truncated": false,
"note": null,
"loadMs": 1820,
"capturedAt": "2026-10-22T15:00:00+00:00"
}

The key-value store also holds SUMMARY (counts and why the run stopped, if it stopped early).

Run outcome

  • A run succeeds if at least one page was captured.
  • A run that captured nothing (every page failed or was blocked) ends as Failed with the reason in the status message, so schedules and integrations don't treat it as a success. The exception is a run stopped by your own maximum cost per run.
  • If the run timeout gets close, remaining pages get skipped rows with no screenshot fee and the run ends cleanly.

API example

Uses apify-client 3.x. On older versions, use run["defaultDatasetId"].

from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("opendata-tools/bulk-website-screenshots").call(run_input={
"startUrls": [{"url": "https://example.com"}, {"url": "https://apify.com"}],
"format": "png",
"device": "desktop",
"fullPage": True,
})
for item in client.dataset(run.default_dataset_id).iterate_items():
print(item["status"], item["url"], item["screenshotUrl"] or item["reason"])

Instant screenshot API (one URL, one image)

Need a screenshot inside your own app, one page at a time? Call the always-on API and get the image straight back. Most answers take 5 to 30 seconds. The first call after an idle spell takes about 20 seconds longer while the API starts. No run to start, no dataset to read.

curl -H "Authorization: Bearer <YOUR_API_TOKEN>" \
"https://opendata-tools--bulk-website-screenshots.apify.actor/screenshot?url=https://example.com&format=png&fullPage=true" \
-o example.png

Python:

import requests
r = requests.get(
"https://opendata-tools--bulk-website-screenshots.apify.actor/screenshot",
params={"url": "https://example.com", "format": "png", "fullPage": "true"},
headers={"Authorization": "Bearer <YOUR_API_TOKEN>"},
timeout=180,
)
if r.status_code == 200:
open("example.png", "wb").write(r.content)
else:
print(r.status_code, r.json())

JavaScript (Node 18+):

const params = new URLSearchParams({ url: "https://example.com", format: "png", fullPage: "true" });
const res = await fetch(`https://opendata-tools--bulk-website-screenshots.apify.actor/screenshot?${params}`, {
headers: { Authorization: "Bearer <YOUR_API_TOKEN>" },
});
if (res.ok) {
const fs = await import("node:fs/promises");
await fs.writeFile("example.png", Buffer.from(await res.arrayBuffer()));
} else {
console.log(res.status, await res.json());
}
  • Same options as above, as query parameters (or a JSON body with POST). hideSelectors is a comma-separated list.
  • response=json saves the file and returns its link plus width, height, page title and final URL, instead of the image.
  • Answers: 200 the screenshot (charged), 422 the page could not be captured (no screenshot fee, though time spent counts as usage; the reason is included), 400 bad input, 429 busy (wait for Retry-After and try again), 402 the maximum charge is reached (nothing charged; see Staying within budget).
  • Image answers carry X-Screenshot-Width, X-Screenshot-Height, X-Screenshot-Final-Url, X-Screenshot-Truncated and X-Screenshot-Note headers. A page captured at the time limit says captured before the page finished loading in X-Screenshot-Note (or in note with response=json).
  • Same price per screenshot as batch runs. The API runs on your Apify account: you pay usage while it handles your requests and for about 60 seconds after the last one, then it shuts down. Failed and blocked pages have no screenshot fee, though time spent counts as usage.
  • Each API instance captures 3 pages at once and queues up to 6 more. Requests beyond that, or ones that would wait more than 60 seconds, get 429 with Retry-After (5 or 10 seconds).
  • Sending more than 9 requests at once can start extra API instances, and each one is billed for its start-up and about 60 seconds of idle time.

Credits

Ad, tracker and cookie-banner blocking uses EasyList, EasyPrivacy and Fanboy's Cookie List (GPLv3 / CC BY-SA 3.0), read by Brave's adblock engine.