Website Screenshots: Full Page, Mobile, PDF & Change Alerts
Pricing
from $3.00 / 1,000 screenshots
Website Screenshots: Full Page, Mobile, PDF & Change Alerts
Screenshot any list of URLs (full page or viewport, desktop or mobile, PNG, JPEG or PDF, one element, no cookie banners), or schedule it to compare each page with the last run and get how much it changed, with the changed area and a Slack-ready summary.
Pricing
from $3.00 / 1,000 screenshots
Rating
0.0
(0)
Developer
Rook Dev
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 hours ago
Last modified
Categories
Share
Get a clean screenshot of every page on your list (full page or window, desktop or mobile, PNG, JPEG or PDF, or just one element), or schedule it to see which pages changed since the last run, by how much and where, with a one-line alert ready for Slack.
- Screenshots without the clutter. Cookie banners (OneTrust, Cookiebot, Didomi, Usercentrics and about 20 more consent tools, plus unknown ones found by their wording) and chat bubbles are hidden, never clicked. Lazy images are scrolled into view first. Full page, window only, a custom width and height, or one element by CSS selector.
- Visual change monitoring built in. Turn on Compare with the previous run, schedule it, and each page gets
changePercent, thechangedAreabox, a link to the previous screenshot and an image with the changes in red. Alerts read "Pricing page on example.com changed 12%". - You never pay for a broken result. Pages that time out, return 404 or 500, ask for a login or show a bot check get a free row saying why. One bad URL never fails the run.
- Fast and bounded. Up to four pages in parallel in one browser, at most 60 seconds per page (waits and delay included), at most 5,000 URLs per run, and a size cap for endless or very wide pages (3,840 px wide, 16 megapixels per image), so a run never hangs or runs out of memory.
Each screenshot is saved in the run's storage and linked from the dataset, so you can open it, download it or pass the link on.
Sample output
A monitoring row from a test run on a demo pricing page whose price box changed colour between two runs:
{"status": "changed","summary": "Pricing page on example.com changed 0.7%","pageId": "example.com/changing","label": "Pricing page","url": "http://example.com/changing","finalUrl": "http://example.com/changing","title": "Pricing","httpStatus": 200,"device": "desktop","fullPage": true,"selector": null,"format": "png","width": 1280,"height": 1200,"pageHeight": 1200,"truncated": false,"screenshotKey": "screenshot-example.com_changing-4c1c059736.png","screenshotUrl": "https://api.apify.com/v2/key-value-stores/Ub4cNs6vXm1Jo8LfG/records/screenshot-example.com_changing-4c1c059736.png","pdfUrl": null,"changePercent": 0.65,"changedPixels": 10000,"changedArea": { "x": 100, "y": 300, "width": 200, "height": 50 },"previousScreenshotUrl": "https://api.apify.com/v2/key-value-stores/Hq3rXy8bM2kTzWv1L/records/base-example.com_changing-b3cfbb10df64-ec1ee42ba8.png","previousCapturedAt": "2026-10-28T07:00:00Z","diffImageUrl": "https://api.apify.com/v2/key-value-stores/Ub4cNs6vXm1Jo8LfG/records/diff-example.com_changing-16fc7c1016.png","capturedAt": "2026-10-29T07:00:00Z","warnings": [],"firstSeenAt": "2026-10-28T07:00:00Z"}
A normal (non-monitoring) run returns the same fields with status: "captured" and the change fields empty. The Screenshots view of the Output tab shows each image as a thumbnail.
Who uses it
- Marketing and product teams: watch competitors' pricing, plans and landing pages and get told the day they change, with the changed area outlined.
- Agencies and SEO teams: dated full-page captures of client and competitor sites, desktop and mobile, for reports and before/after comparisons.
- Developers and QA: a visual check of production pages after each deploy; a changed percentage above your threshold is your regression alarm.
- Compliance and legal teams: a dated visual record of terms, disclosures and price pages, and an alert when they change.
- AI agents: a narrow, predictable tool for "show me this page" or "did this page change?"; works through the Apify MCP server.
How to use it
- Paste pages into URLs to capture, one per line. Add a name for alerts with
Name | URL, for exampleCompetitor pricing | https://example.com/pricing. - Pick the Device (desktop, mobile or tablet), Full page or window only, and the Image format. Optionally capture one element with a CSS selector, or wait for an element before capturing.
- Click Start. Open the screenshots from the Output tab, or read the rows (with links) as JSON, CSV or Excel or through the API.
To monitor: turn on Compare with the previous run, click Save as a new task and add a schedule (daily or hourly). The first run saves each page as its baseline (or, with Start quietly, saves it without returning rows); from then on every page is compared with its last reported screenshot. Turn on Only return pages that changed if you want rows for changes only.
Input examples
Screenshots of three pages on a phone, as JPEG, plus PDFs:
{"urls": ["https://apify.com", "https://apify.com/pricing", "https://docs.apify.com"],"device": "mobile","format": "jpeg","savePdf": true}
Daily monitor of competitor pricing tables, changes only, ignoring a rotating banner:
{"urls": ["Acme pricing | https://acme.example/pricing", "Globex plans | https://globex.example/plans"],"selector": "#pricing","hideSelectors": [".promo-carousel"],"compareWithPreviousRun": true,"onlyChangedPages": true,"skipExistingOnFirstRun": true}
Main inputs
| Input | What it does | Default |
|---|---|---|
urls | Pages, one per line; Name | URL adds a name for alerts | required |
fullPage | Whole page, or only the window | on |
device | desktop 1280 x 800, mobile 412 x 915 at 2x, tablet 800 x 1280 at 2x | desktop |
viewportWidth, viewportHeight | Custom window size, e.g. 1440 x 900 | device preset |
format, jpegQuality, savePdf | PNG or JPEG; also an A4 PDF | PNG |
selector / waitForSelector | Capture one element / wait for one before capturing | none |
hideCookieBanners, hideSelectors | Hide banners and chat widgets; hide your own elements | on / none |
darkMode, delayMs | Dark theme; extra wait up to 10 s (inside the page time limit) | off / 0 |
maxPageHeight, pageTimeoutSecs | Height cap (10,000 px); time limit per page, waits and delay included (45 s, at most 60) | |
compareWithPreviousRun, minChangePercent | Monitoring; smallest change to report | off / 0.1% of the page |
Output
One row per page. status is captured in a normal run, or new, changed or unchanged when monitoring. Every row has a one-line summary, the screenshotUrl, the image size, the page's final URL, title and HTTP status, and warnings (for example when a page was cut at the height cap: truncated: true). Monitoring rows add changePercent, changedPixels, changedArea (x, y, width, height in screenshot pixels), previousScreenshotUrl, previousCapturedAt, diffImageUrl and firstSeenAt.
Pages that produced no screenshot get a free row with status not_found, blocked (access denied or a bot check), login_required, element_not_found, invalid_input, error or skipped (not tried: your spending limit was reached, the run stopped early, or the list was over 5,000 URLs; one row covers all of them), and a plain-English error. When monitoring, a page that suddenly renders blank (one colour) gets a free blank_page row and keeps its baseline; only if it is still blank on the next run is it reported as changed, so a passing glitch never sends a false alert. A RUN_SUMMARY record in the run's key-value store lists every page with its result.
Get alerts in Slack, Google Drive, Make, Zapier or n8n
- Turn on monitoring, click Save as a new task, and add a schedule to the task.
- On the task's Integrations tab, pick Slack, connect your workspace, choose the Run succeeded event and a channel, and paste this as the message:
*{{resource.statusMessage}}*<https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}|Open the screenshots>
The status message reads like Checked 20 page(s): 2 changed, 18 unchanged, 0 new. Acme pricing on acme.example changed 12%; Globex plans on globex.example changed 0.4%., so the alert itself says what changed.
- Google Drive: the Upload results to GDrive integration saves each run's rows (with the image links) as CSV or JSON.
- Make, Zapier, n8n: use the Apify app's "Actor run finished" trigger, filter rows on
status = changed, and post thesummarywith thediffImageUrl. - Webhooks: send the run to your own endpoint and read the dataset through the API.
Pricing
Pay per event. No platform usage charges on top: the price is the price.
| Event | Price |
|---|---|
| Screenshot delivered (a row with a screenshot; PDF included) | $3.00 per 1,000 ($0.003 each) |
| Page compared with its previous screenshot (monitoring only) | $2.00 per 1,000 ($0.002 each) |
| Run start | $0.0001 per GB of run memory: $0.0002 per run at the default (and maximum) 2 GB, $0.0001 at 1 GB |
Pages that fail, need a login, are blocked, are not found, render blank or are skipped are free, and so are pages a run did not get to when it stopped early. A comparison is charged only after the page's row was delivered.
- One-off batch: 500 product pages, full page on desktop: 500 x $0.003 + $0.0002 = $1.50.
- Daily monitoring: 20 competitor pages every day with Only return pages that changed, about 2 changes a day: 20 x $0.002 + 2 x $0.003 + $0.0002 = $0.0462 a day, about $1.39 a month. With a row for every page every day: about $3.01 a month.
- One screenshot through the API: $0.003 + $0.0002 = $0.0032.
Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached: pages it could not try get a free skipped row, and a change it could not deliver is reported on the next run. Nothing is charged twice.
FAQ
Is this allowed? It opens only the public pages you list, once per run, the way a person opens them in a browser: it does not crawl, follow links, log in, fill in forms, click consent buttons or try to get past bot checks. Because it never crawls, it does not apply robots.txt crawl rules, the same as Apify's own Website Screenshot Generator. Follow the terms of the sites you capture. The output holds only what you asked for: page URLs, titles and links to images of your target pages; it extracts no text or personal data from them, but a screenshot shows whatever the page shows, so do not point it at pages with personal data you have no reason to keep. A screenshot is a copy of the page: the content stays the site's copyright, so capture pages you may lawfully record (your own pages, or competitors' public pages for internal reference) and do not republish the images.
Why did a page get a blocked or login_required row? The site refused access (HTTP 403), showed a bot check, redirected to a login page or asked for a password, or the page (or a frame in it) led to a private network address. Pages behind a login are never captured, and you are not charged. This version uses no proxies, so sites that block cloud browsers stay blocked.
Why was a run stopped early, and what does it cost? Three safety limits stop a run before it has opened every page: your own maximum cost per run; 50 pages in a row that could not be captured (the list or the sites are probably down); and a limit on browser time that produced no paid results (about 7 minutes at 2 GB, more once pages are delivered, so normal runs never reach it). The pages it did not get to are not opened and cost nothing; they share one free skipped row, the status message says why, and RUN_SUMMARY lists them. Run them again later, or split a large list across tasks. A page that makes more than 1,500 requests (it never stops loading) is stopped and gets a free error row. If one Apify account's runs keep producing nothing to charge, new runs from that account stop early for up to 24 hours, with a free row that says so; normal use never gets near this.
Can it reach private or internal addresses? It is built not to, and never gives you what it finds there. URLs with private, local or reserved addresses are refused in any spelling (10.0.0.1, 127.1, 0x7f.0.0.1, 169.254.169.254 and so on). Before the browser sends any request, its host is looked up and the request is refused if the address is private or cannot be looked up. Afterwards the real server address of every response is checked, as are redirects and frames, so a site whose name only seems public (DNS rebinding) or that redirects inwards gets a free blocked row instead of a screenshot. WebSockets and popups are never opened. One limit remains: in the rebinding case the browser has already sent its requests to that address before the capture is refused.
Why does a page show as changed on every run? Something on it moves: a carousel, an ad, a "posted 5 minutes ago" line, a live counter. Open diffImageUrl to see what, then hide it with Hide these elements (the best remedy for carousels, dates and ads), capture only the part you care about with Capture one element, or raise Smallest change to report. CSS animations and the text cursor are frozen for every capture, colour differences of up to 24 of 255 (font smoothing, image re-encoding) do not count, and changes under 10 pixels or under 0.1% of the page (the default) are not reported.
How is the change measured? The two screenshots are compared pixel by pixel in the browser (a pixel counts when any colour channel moved by more than 24 of 255). changePercent is the share of changed pixels over the larger of the two images, so a page that grew taller counts its new part as changed. By default a page counts as changed from 0.1% of its pixels, about a changed paragraph or banner on a full page; a changed price is smaller, so capture the price element (then the share is of that element) or choose Any visible change. The baseline moves only when a change is reported, so small changes that each stay below your threshold add up until they are reported.
What happens with very long or very wide pages? Full-page captures stop at Max page height (10,000 px by default, up to 16,000) and the row says truncated: true. To fit in memory, every image also stays under 16,000 pixels tall and 16 megapixels in all, and pages are captured at most 3,840 px wide: mobile stops at 8,000 px, tablet at 5,000 px and a 3,840 px wide window at about 4,100 px. Pages taller than the cap are captured in a cheaper way, by enlarging the window to the captured height (about 1 second instead of 15 to 30 for a very long page); sections sized to the window height, such as a 100vh hero, are pinned to their normal size first, but other window-relative layouts can look slightly different from a normal full-page capture. Infinite-scroll pages stop at the cap too, and lazy-image scrolling stops after 6 seconds. A PNG over 15 MB (a page full of photos) is saved as a JPEG, and the row says so.
Does it accept cookie banners for me? No. Banners and their dark overlays are hidden, nothing is clicked, so no consent is given on your behalf. If a site only shows its content after consent, turn Hide cookie banners off to see the page as visitors first see it.
How long are screenshots kept? In the run's storage for your plan's data retention period. When monitoring, each page's last two screenshots stay in the monitoring memory (a named key-value store), so the previousScreenshotUrl of the latest change keeps working; older ones are deleted, so a previousScreenshotUrl in an older row stops working after two more changes of that page (download what you want to keep). Pages that no monitoring run has checked for 30 days are forgotten (their record and images are deleted), so a monitor scheduled at least every 30 days keeps its memory; the rest of the store stays until you delete it, and deleting it in Storage starts every monitor afresh.
Why is the first monitoring run different? There is nothing to compare with yet, so each page is saved as its baseline (new). With Start quietly, the baselines are saved without rows and each is charged as one comparison instead of a screenshot. Each saved task, and each set of capture options (device, size, element, hidden elements, threshold), keeps its own memory, and normal runs never touch it.
Switching from apify/screenshot-url? Put your URLs one per line. Through the API, its field names delay, scrollToBottom and selectorsToHide are understood too, and its format: "pdf" gives a PNG screenshot plus the PDF (the same as Also save a PDF).
Can I use it from code or an AI agent? Yes: through the Apify API, the Python and JavaScript clients, or the Apify MCP server. Inputs and outputs are stable and documented above.
Support
Open an issue on the Issues tab with the URL and your input; I aim to answer within a day.