After a docs change, a pricing-page update or a client preview goes live, someone usually asks: can you show me what the public page looked like on desktop and on a phone? A screenshot dropped into Slack answers the visual question. It doesn't answer the follow-ups: is this the file we archived, when was it taken, which viewport produced it, and did the page actually load or was it a 404?

This walkthrough uses the Website Screenshot Evidence Packs Actor from SteadyScrape (steady-scrape) on Apify. You give it up to five public http or https URLs. For each one it saves a PNG in a fixed viewport, the SHA-256 of that file, the page's HTTP status and final URL, a UTC time from the run's clock, a JSON manifest and a ZIP. The result is an ordinary webpage capture with integrity hashes. It is not notarized, not legal proof, and not a guarantee of historical truth.

The problem

QA engineers, agency account teams and developers need a repeatable way to:

  • Capture the same public URL at a fixed desktop size and a fixed mobile size.
  • Hand a teammate both the PNG and a checksum they can recompute.
  • Know whether the page really answered 200, or whether the "screenshot" is of an error page or a redirect.
  • Keep a manifest that says what happened to every attempt.

It's a poor fit for logged-in pages, personal profiles or long-term legal archives. The Actor opens each URL directly. It doesn't crawl links, submit forms, log in, solve captchas or run JavaScript you supply. It also doesn't try to decide whether a 200 page is a login wall or a challenge page; it records the status the server sent.

What a capture record contains

Each dataset row is one URL and one viewport.

Field What it means
status captured, partial, refused, blocked, unsupported, failed, empty or limit_exceeded
url, finalUrl Sanitized URL you asked for, and where the page ended up after redirects
viewport desktop or mobile
httpStatus HTTP status of the main page, recorded by the browser session
sha256 SHA-256 of the stored PNG
capturedAt UTC time from the run's clock, not a timestamp authority
byteLength, width, height Size of the stored PNG
refusedSubresourceCount Requests the browser refused because they pointed at private or local addresses
usable true only for a 2xx page with a stored PNG and nothing refused
kvKey, archiveEntry Where the PNG lives in the run's key-value store and in the ZIP
reasonCode Why a row is partial, refused or failed

The run summary is in OUTPUT. The ZIP holds manifest.json plus PNGs named like captures/01-desktop-<hash>.png. It contains no HTML or scripts.

Running it in Apify Console

  1. Open Website Screenshot Evidence Packs.
  2. Paste one to five public URLs. Don't add usernames, passwords or tokens; credential URLs are refused.
  3. Set viewports to desktop, mobile or both. Desktop is 1280×800, mobile is 390×844.
  4. Leave fullPage off for a viewport-only shot. With it on, the capture is the full page, cut off at 4,000 pixels tall.
  5. Run it, then open the dataset (one row per URL and viewport) and the key-value store (OUTPUT, the manifest, the PNGs and the ZIP).
{ "urls": ["https://example.com/"], "viewports": ["desktop", "mobile"], "fullPage": false } 

Calling it from Python

import os from apify_client import ApifyClient client = ApifyClient(os.environ["APIFY_TOKEN"]) run = client.actor("steady-scrape/website-screenshot-evidence-packs").call( run_input={ "urls": ["https://example.com/", "https://www.iana.org/help/example-domains"], "viewports": ["desktop", "mobile"], "fullPage": False, } ) for row in client.dataset(run["defaultDatasetId"]).iterate_items(): print(row["status"], row["viewport"], row.get("httpStatus"), row.get("sha256"), row["url"]) 

To check a file later, download the PNG (or the ZIP from the archiveKey in OUTPUT) and compare:

sha256sum captures/01-desktop-*.png 

A real row

From a public run on https://example.com/ (desktop):

{ "url": "https://example.com/", "finalUrl": "https://example.com/", "viewport": "desktop", "status": "captured", "reasonCode": null, "httpStatus": 200, "capturedAt": "2026-11-10T22:49:06.620Z", "sha256": "9dab6e635ca5a3f4c2220115121db3387da42e6890aae19da30d0395e2039066", "byteLength": 74812, "width": 1280, "height": 800, "fullPage": false, "archiveEntry": "captures/01-desktop-9dab6e635ca5.png", "refusedSubresourceCount": 0, "usable": true } 

If a URL points at a private address (say http://169.254.169.254/), the row is refused with reasonCode non_public_address and nothing is fetched. A page that answers 404 or 500 still gets a PNG, but the row is partial with not_success_http_status.

Re-running later against a live page can produce different bytes and a different sha256. That's the point: the hash tells you exactly which file you're looking at.

What it costs

Pay per event:

  • $0.003 per usable screenshot (one viewport of a page that answered 2xx, with nothing refused).
  • $0.003 once per run that saved at least one usable screenshot.
What you run Math Total
1 URL, desktop only 1 × $0.003 + $0.003 $0.006
1 URL, desktop + mobile 2 × $0.003 + $0.003 $0.009
5 URLs × both viewports 10 × $0.003 + $0.003 $0.033

Refused URLs, failed loads, timeouts, non-2xx pages and partial captures are free, and a run with no usable screenshot pays nothing. There's no start fee or compute charge on top, and you can set a maximum charge per run.

Limits

  • Up to 5 URLs and 10 screenshots per run, one at a time.
  • Desktop 1280×800, mobile 390×844, device scale 1.
  • About 15 seconds to load each page; 180 seconds per run.
  • Public http/https only. Userinfo, credential query keys, localhost, .local, .internal and private or reserved IPs are refused.
  • Retention follows your Apify plan. The manifest's timestamp doesn't delete anything, so download the ZIP if you need it longer.

Scheduling and integrations

Start runs from Console, the Apify API or an Apify schedule (for example, every Monday on your top pages). Make, Zapier and n8n Apify modules, or Apify's MCP server for AI agents, can start the same run.

FAQ

Is a screenshot legal proof?
No. It's a webpage capture with a hash of the bytes this run stored, timed by the run's clock.

Can I screenshot a logged-in page?
No. Cookies, headers, passwords and proxy credentials aren't accepted.

Am I charged for a 404 or a failed page?
No. Only 2xx pages with a stored PNG and nothing refused are charged.

What sizes are desktop and mobile?
1280×800 and 390×844, fixed.

Try it: Website Screenshot Evidence Packs on Apify. More tools from SteadyScrape (steady-scrape) are on our Apify profile.