Website Screenshot & PDF: Full Page, Most Cookie Banners Hidden
Pricing
$2.00 / 1,000 screenshot or pdf files
Website Screenshot & PDF: Full Page, Most Cookie Banners Hidden
Screenshots (PNG, JPEG, WebP) and PDFs of web pages or HTML. Full page with lazy images, mobile presets, common cookie banners hidden. You pay only for files produced.
Pricing
$2.00 / 1,000 screenshot or pdf files
Rating
0.0
(0)
Developer
Francesco Antonio Russo
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Website Screenshot & PDF – Full Page, Mobile, Common Cookie Pop-ups Hidden
Give it web addresses (or a piece of HTML) and get back a PNG, JPEG, WebP or PDF file for each one: the whole page with its lazy-loaded images, as a desktop, phone or tablet, with the common cookie banners and consent walls hidden. You pay only for a file that was produced and is not empty: a 404, a blocked page, a timeout or a bad address costs nothing.
For people who need page images or PDFs at scale: archiving and compliance snapshots, link previews, visual checks of a website on many screens, invoices and reports turned from HTML into PDF, thumbnails for a catalogue.
Price
One event: Screenshot or PDF file, $2 per 1,000 files ($0.002 per file). The event is charged only when a file was produced, is not empty and is stored in the key-value store of the run.
| What you run | Files produced | Cost |
|---|---|---|
| The prefill (2 pages) | 2 | $0.004 |
| 100 pages | 100 | $0.20 |
| 1,000 pages, 20 of them are 404 | 980 | $1.96 |
| 5,000 pages | 5,000 | $10.00 |
| 10,000 pages | 10,000 | $20.00 |
Free, never charged: an address that is not a valid web address (invalid_url), an address that is not http or https (for example ftp://) or that points inside a private network (blocked_unsafe_url), a host that does not exist or refuses the connection (unreachable), a page that answers an error such as 404 or 500 (http_error), a login wall, rate limit or bot challenge (blocked), a page that does not load in time (timeout), a capture that could not produce a file (empty_capture), and a page that came out as one flat colour with nothing on it (blank). Each of them still gets a record that says why. The spending limit you set on the run is respected exactly: the run stops before a file that would go over it. Apify's own platform fees, if any, are on the Pricing tab of this Actor.
What you can set
| Input | What it does |
|---|---|
urls | The addresses to capture (a list of addresses, or of {"url": "..."} objects). Only http and https. |
html | A complete HTML page to render instead of, or besides, the addresses (an invoice, a report). Relative addresses inside it cannot be resolved: use inline styles and data: or absolute https images. |
format | png (default), jpeg, webp or pdf. |
fullPage | Capture the whole page (default on) or only the first screen. |
maxFullPageHeight | Tallest capture in CSS pixels, up to 16,384 (WebP: 16,383). A taller page is cut there and marked truncated. |
quality | JPEG and WebP quality, 1 to 100 (default 80). |
device | desktop 1440x900 (default), desktop_hd 1920x1080, laptop 1280x800, iphone_15, iphone_se, pixel_8, ipad. Phones and the tablet also get a mobile browser identity and touch. |
width, height | Your own screen size; they replace the preset (the record then says custom). |
deviceScaleFactor | Pixel density 1 to 3 (2 = retina: twice as wide and tall). Default 1. |
colorScheme | light (default) or dark, for sites that follow the system setting. |
selector | Capture only one element, for example #pricing-table. |
waitUntil | load (default), domcontentloaded or networkidle (waits for a quiet network for at most 15 more seconds, and captures anyway if the page never gets quiet). |
waitForSelector | Wait (up to 20 seconds) for an element to exist before capturing. |
delayMs | Extra wait in milliseconds, 0 to 10,000. |
scrollToBottom | Before a full page, a PDF or an element, scroll the page step by step so lazy-loaded images load (default on; at most 12 seconds and never beyond the height cap). |
cookieBanners | hide (default) or off. Never "accept": this Actor gives no consent on your behalf. |
hideSelectors | Your own list of CSS selectors to hide (chat widgets, sticky bars). |
customCss | CSS added to the page before the capture. |
blockAds | Do not load a short fixed list of advertising networks (about 25 domains). Default off. |
pdfFormat, pdfLandscape, pdfMargins, pdfPrintBackground, pdfMedia | PDF paper size (A3, A4, A5, Letter, Legal, Tabloid), orientation, margins (default 10 mm), backgrounds (default on), and whether the print styles (default) or the screen styles are used. |
cookies, headers, basicAuth | Your own session cookies, HTTP headers and basic-auth login for pages you may capture. Stored as secrets, never written to the log or the output. Cookies need a domain or url and are sent only there; headers and basic auth are sent only to the address of the page itself, never to the third-party files it loads. |
timeoutSecs | Time given to one address, 2 to 120 (default 45). A timeout, a reset connection or a 5xx answer is tried once more. |
maxConcurrency | Pages in parallel, 1 to 6 (default 3). |
Options that do not apply to the chosen format (for example selector with pdf) are listed as warnings in the log and in the SUMMARY.
Example input (this is the prefill):
{"urls": [{ "url": "https://www.python.org/" },{ "url": "https://en.wikipedia.org/wiki/Web_scraping" }]}
What you get
One record per address (and one for the html input) in the dataset, and one file per record in the key-value store of the run. Records are written as captures finish: index is the position of the entry in your input.
| Field | Meaning |
|---|---|
index | Position of the entry in your input, counting from 0 (an address repeated later in the list has no record of its own, so the numbers can skip; the HTML page, if any, comes after all the addresses). |
inputType | url or html. |
url | The address you gave (null for HTML). |
finalUrl | Where the page ended after redirects. |
status | ok or the free reason (see below). |
httpStatus | HTTP status of the page itself. |
title | Title of the page. |
format | png, jpeg, webp or pdf (null when there is no file). |
fileUrl | Public link to the file (works without a token; it lives as long as the storage of the run). |
fileKey | Key of the file in the key-value store. |
bytes | Size of the file. |
width, height | Size of the image in pixels as produced (CSS pixels times deviceScaleFactor); null for PDF. |
fullHeightPx | Height of the whole page in CSS pixels. |
truncated | True when the page was taller than the cap and the image was cut. |
pages | Number of pages of a PDF. |
device | The preset used, or custom. |
durationMs | Time spent on this entry. |
error | Why there is no file. |
capturedAt | When the record was written. |
A real record (from a run on 2026-10-15; the link of your run has your store id and a signature):
{"index": 0,"inputType": "url","url": "https://www.python.org/","finalUrl": "https://www.python.org/","status": "ok","httpStatus": 200,"title": "Welcome to Python.org","format": "png","fileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/shot-0001-www-python-org.png?signature=<signature>","fileKey": "shot-0001-www-python-org.png","bytes": 488595,"width": 1440,"height": 2549,"fullHeightPx": 2549,"truncated": false,"pages": null,"device": "desktop","durationMs": 4209,"error": null,"capturedAt": "2026-10-15T09:10:08.749Z"}
A free record, from the same kind of run:
{"index": 5,"inputType": "url","url": "https://httpbin.org/status/404","finalUrl": "https://httpbin.org/status/404","status": "http_error","httpStatus": 404,"title": null,"format": null,"fileUrl": null,"fileKey": null,"bytes": 0,"width": null,"height": null,"fullHeightPx": null,"truncated": false,"pages": null,"device": "desktop","durationMs": 642,"error": "the page answered HTTP 404","capturedAt": "2026-10-14T15:32:07.789Z"}
status | Meaning | Charged |
|---|---|---|
ok | A file was produced and stored. | yes |
invalid_url | Not a web address (or credentials inside the address: use basicAuth). | no |
unreachable | The name does not resolve, or the host refused or dropped the connection. | no |
http_error | The page answered 4xx or 5xx other than 401, 403 and 429. No file is saved, not even of the error page. | no |
blocked | 401, 403, 429, or a bot challenge page ("Just a moment", "Attention Required", or a CDN challenge that answers 202 with an empty page, or a short block page that answers 200 with a known sentence such as "You've been blocked by network security" or "Access denied"). A page with a lot of text is never taken for a block page because it contains those words. No attempt is made to get around it. | no |
timeout | Not captured within timeoutSecs (after one more attempt), or waitForSelector never showed up. | no |
blocked_unsafe_url | Not http or https, or an address inside a private network, loopback, link-local or cloud-metadata range, also after redirects. | no |
empty_capture | The browser could not produce a file: the selector matches nothing, the address is a file download, the tab crashed, or the image came out empty. | no |
blank | The picture of the page is one flat colour (99.9% of the pixels) and the page has no text and no pictures of its own, typically a challenge page or a page that never rendered. Only whole-page images are judged (not PDF, not a selector capture); a page with any text, logo or picture is never blank. If it is a page that needs more time, raise delayMs. | no |
A run also writes a SUMMARY record: files produced, files charged, free records by reason, why the run stopped (done, budget exhausted, time limit, aborted), warnings and a few counters. If every address of a run is invalid, the run still ends normally with one free record per address.
How it works, and what it will not do
It opens each page in Chromium (Playwright), waits for it, scrolls through it so lazy images load, hides what you asked to hide and takes the picture or prints the PDF. It identifies itself honestly: the user agent of Chromium plus (compatible; WebsiteScreenshotPDF/0.1), the name of this Actor (https://apify.com/far003/website-screenshot-pdf). The link is not in the user agent because some publishers refuse any agent that names a scraping platform.
It does not: solve CAPTCHAs, mask itself as a normal browser, rotate proxies, fake a location, click "Accept" on consent banners, or sign in for you. A page behind a login is captured only with the cookies, headers or login that you provide. Some sites refuse automated visitors that say who they are; you then get a free blocked record (in a test on 2026-10-14 a large news site and a photo site both answered 403).
Safety. Every connection of the browser goes through a filter that resolves the name itself and connects only to a checked public address. Private networks (10.x, 172.16-31.x, 192.168.x), loopback, link-local and cloud metadata addresses are refused for the page you ask for, for its redirects and for everything the page loads, so an address you give, or a page you capture, cannot be used to reach into the machine that runs the Actor. Mail, shell and database ports are refused too.
Memory and speed
Default memory is 2,048 MB (minimum 1,024, maximum 4,096). Peaks measured on a laptop (macOS, resident memory of Node plus Chromium; Linux numbers can differ):
| Run | Peak |
|---|---|
| The prefill, 2 pages, 3 in parallel | about 1.1 GB |
| 3 very tall pages (cut at 16,384 px), density 1, run memory 2,048 MB | about 1.8 GB |
One 1440 x 16,384 page at deviceScaleFactor 2 (image 2880 x 32,768) | about 2.3 GB |
| 3 of those, run memory 4,096 MB | about 3.3 GB |
Pages load in parallel, but turning a tall page into an image waits its turn when the memory of the run would not hold both: this is why a run with little memory is slower, not killed. A single capture that is bigger than the memory of the run (retina on a very tall page with 2,048 MB) runs alone and may still run out of memory: use 4,096 MB for that, or lower deviceScaleFactor or maxFullPageHeight. A typical page takes 2 to 12 seconds; a very tall page about 10 seconds.
Limits, honestly
- Height. Chromium cuts or empties images taller than 16,384 pixels (WebP: 16,383 pixels of the file: with
deviceScaleFactor2 a WebP is cut at 8,191 CSS pixels, with 3 at 5,461). Such a page is captured up to the cap and the record saystruncated: trueand gives the real height infullHeightPx. Use a PDF to get a very long page whole. - Single-page apps. With the default
waitUntil: loadan app that draws its content after the page has loaded can be captured as its loading skeleton, and it is charged as a file. If the pictures show placeholders, usewaitUntil: networkidle(orwaitForSelectoron an element that only exists once the content is there). - PDF of a long page. The PDF is what the browser prints. A page that scrolls inside its own container (a fixed-height app or an article frame) or whose print style hides the content can come out as one page; the record gives the number of pages in
pages: check it, or trypdfMedia: screen. - Lazy loading is woken by scrolling, with a time cap. A page that grows forever (endless scroll) is captured up to the cap; content that needs a click, a login or a long animation may not be there. Sections sized to the screen height (
100vh) can look stretched in a full-page image. - Cookie banners are hidden by a list of the common consent platforms (OneTrust, Cookiebot, Didomi, Usercentrics, Quantcast, Sourcepoint and others); it was checked on real pages for OneTrust and Cookiebot, and the consent walls that cover the whole page and lock its scroll of Le Monde, Le Figaro, Marca, Corriere della Sera and El País are hidden too (the page is freed, so a full page is captured). The list is not complete. A banner that is not recognised stays; send its selector in
hideSelectors. The list never hides a page wrapper, the body or anything that contains the main content. - A login redirect is a page. A page that sends you to a login page is captured as that login page (
status: ok, andfinalUrlshows it): checkfinalUrlif that matters to you. It is charged like any file. - HTML input cannot load relative files. The sandbox page has no real address.
- DNS. The address check and the connection use the same checked address, so a name that changes its answer in the middle cannot slip a private address in.
- No guarantee from the sites. Pages change, block or go down; a file reflects the page at the moment of the capture.
FAQ
Do I pay for pages that fail? No. Only a produced, non-empty, stored file is charged. You can see every free record and its reason in the dataset.
Why did I get blocked? The site answered 401, 403 or 429, or showed a bot challenge or a short block page (also when its code is 200). This Actor does not try to get past that. If you are allowed to see the page, pass your own cookies, headers or basicAuth.
Can it capture pages behind a login? Yes, with your own session cookie (or header, or basic auth). They are secret inputs and are never logged or put in the output.
Does it accept cookie banners? No. It hides the common ones, because accepting is a consent that only you can give. Use cookieBanners: "off" to see the page as a first-time visitor does.
How do I get a mobile screenshot? Set device to iphone_15, iphone_se, pixel_8 or ipad, and deviceScaleFactor to 2 for a sharp image.
How do I turn an invoice into a PDF? Put the HTML in html, set format to pdf, and choose pdfFormat and pdfMargins. Styles inline or in a <style> tag; images as data: addresses.
The page is taller than 16,384 pixels. The image is cut at the cap and marked truncated. For the whole page use pdf, or capture sections with selector.
How long are the files kept? In the key-value store of the run, for as long as your Apify plan keeps the storage of runs. Download or copy what you need to keep.
Call it from code
Replace <APIFY_TOKEN> with your token.
curl -X POST "https://api.apify.com/v2/acts/far003~website-screenshot-pdf/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"urls": [{"url": "https://example.com"}], "format": "webp", "device": "iphone_15"}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<APIFY_TOKEN>' });const run = await client.actor('far003/website-screenshot-pdf').call({urls: [{ url: 'https://example.com' }],format: 'png',fullPage: true,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const item of items) console.log(item.status, item.fileUrl);
Your responsibility
You are responsible for having the right to capture the pages you give, and for what you do with the files: copyright, terms of the sites and privacy law (such as the GDPR) apply to a screenshot as to any copy. This Actor does not look for personal data and stores nothing but the file you asked for and the record that describes it.