Website Screenshot Generator
Take website screenshots in bulk. Give a list of URLs, get a PNG or JPEG per page. Full page or viewport, 320-2560px, dark mode, HTTP status.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
urls,fullPage,viewportWidth(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.003 per screenshot taken = $3 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Screenshot taken | Charged once per rendered screenshot delivered to the dataset: the image file and its direct link, the final URL after redirects, the page title, the HTTP status, the image and page dimensions, the file size and the render time. Pages that time out, refuse the connection, fail DNS, return an anti-bot check or match no element for your selector are never charged, and neither are duplicate URLs, the sample row an empty run returns, or pages skipped once the run hits your image-size budget. | $0.003 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-08-17, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
urls | One or more pages to photograph, one per line. A bare domain like example.com works too. Up to 300 per run, and duplicates are dropped before anything is charged. | array |
fullPage | Off: you get exactly the viewport, like the first screen a visitor sees. On: the page is scrolled and stitched into one tall image, down to the height ceiling below. | boolean |
viewportWidth | Browser window width the page is laid out at. 1440 is a typical laptop, 1920 a desktop monitor, 390 a phone. Between 320 and 2560. | integer |
viewportHeight | Browser window height. This is the image height unless "Capture the whole page" is on. Between 200 and 4000. | integer |
deviceScaleFactor | 1 for a normal screen, 2 for a retina-quality image at twice the pixels on each axis, 3 for print. Remember that 2x is four times the file size and 3x is nine times. | integer |
darkMode | Tells the page the visitor prefers a dark colour scheme. Sites that ship a dark theme will render it; sites that do not are unchanged. | boolean |
mobile | Renders as a touch phone with a mobile user agent, so you get the responsive layout. Pair it with a narrow viewport width such as 390. | boolean |
format | JPEG is far smaller and right for almost everything. PNG is lossless and supports a transparent background. | string |
quality | Only applies to JPEG. 80 is visually clean at about a third of the size of 100. Lower it to fit more pages inside the run's size budget. | integer |
waitUntil | How ready the page must be before the shutter fires. "domcontentloaded" is the fast default. "load" waits for images and stylesheets. "networkidle" waits for traffic to stop, which on an ad-funded page can mean waiting for the timeout. "commit" fires almost immediately. | string |
delayMs | Pause after the page is ready, to let animations finish and fonts swap in. 800 ms suits most sites; raise it for a heavy single-page app. | integer |
navigationTimeoutMs | Give up on a page that has not become ready in this long. The page still gets an uncharged row explaining the timeout. | integer |
scrollToBottom | Scrolls to the foot of the page first so lazy-loaded images below the fold are actually there in the picture. Turned on automatically when you capture the whole page. | boolean |
maxFullPageHeight | The tallest a whole-page capture may get before it is cut off. Protects you from an endless feed turning into a 60,000 pixel image. Rows tell you when a picture was cut. | integer |
maxTotalImageMb | The run stops taking pictures once it has written this many megabytes and writes one uncharged row saying so. Nothing is charged for the pages it skipped. | integer |
maxItems | Hard stop on how many pages this run will photograph, whatever the list length. Keep it low while you are testing - you pay per screenshot. | integer |
selector | A CSS selector. Fill it in to photograph just that element - a pricing table, a hero banner, a chart - instead of the page. Left empty, the whole viewport or page is captured. | string |
hideSelectors | CSS selectors to make invisible before capturing, while keeping the space they occupy. Good for a sticky header you want gone without the layout shifting. | array |
removeSelectors | CSS selectors to take out of the layout entirely. This is the one for cookie banners, chat bubbles and newsletter pop-ups. | array |
customCss | Raw CSS injected just before the picture is taken. Anything you can write in a stylesheet works, up to 4,000 characters. | string |
transparentBackground | PNG only. Leaves the page background out of the image, which is what you want when capturing a single element to drop into a design. | boolean |
locale | The language the browser asks for, as a code such as en-US, de-DE or fr-FR. Sites that localise will render in that language. | string |
timezone | An IANA time zone such as Europe/Berlin or America/New_York, for pages that print dates or clocks. | string |
concurrency | How many pages render simultaneously. 3 is a good balance. Lower it to 1 if a site you are capturing objects to several requests at once. | integer |
useProxy | Turn it on if a site you are capturing refuses the run. | boolean |
proxyUrls | Leave this empty unless you already pay for proxy servers and want the traffic to leave through them, in the form http://user:pass@host:port. Anything listed here takes precedence. | array |
What you get
A structured dataset — each result includes fields like:
urlscreenshotUrlfinalUrltitlestatusCodeimageWidthimageHeightformatfullPagefileSizeBytesloadTimeMsExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Developer & Research Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
GitHub Scraper
Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.
Stack Overflow / Stack Exchange Scraper
Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.
Package Registry Scraper (npm + PyPI)
Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.
arXiv Scraper
Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.
OpenAlex Scholarly Works Scraper
Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.
Crossref Scholarly Works Scraper
Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.
Where this tool sits
- Categories
- Developer & Research Tools
Website Screenshot Generator: a picture of every page in your list, PNG or JPEG
Paste a list of addresses and you get an image of each page, plus a row carrying the link to that image, the final URL after redirects, the page title, the HTTP status, the dimensions and the file size.
You control what the camera sees: the first screen or the whole page stitched together, any width from 320 to 2,560 pixels, normal or retina density, light or dark, desktop or phone layout. CSS selectors let you delete the cookie banner and the chat bubble before the shutter fires, or photograph one element on its own.
Worth knowing before the first run. A page that answers 404 or 500 and still renders is a real screenshot and is charged like any other, with statusCode on the row to tell you. A page that refuses to render at all still gets its image stored and handed to you, and that one is not charged.
| Input | Page addresses, up to 300 per run |
| Output | One row per page, with a link to its image |
| Ceiling | 300 pages per run, inside a total image size budget you set |
| Account needed | None |
| Price | $3.00 per 1,000 screenshots, which is $0.003 each, flat on every plan |
📸 What Website Screenshot Generator does
It opens each address in a real browser at the size you asked for, waits as long as you told it to, applies your CSS, and takes the picture. Images land in the run's key-value store and every row links straight to its own.
Full-page capture scrolls the page and stitches it into one tall image, up to the height ceiling you set, and marks truncated when it had to cut. Scrolling also happens on request without full-page capture, which is what makes lazy-loaded images below the fold actually appear.
The selector fields are where most of the work happens in practice. removeSelectors takes an element out of the layout, which is the one for cookie banners and pop-ups. hideSelectors makes it invisible but keeps its space, which is right for a sticky header you want gone without everything shifting up. selector narrows the shot to one element.
locale and timezone are passed to the browser, so a site that localises renders in the language and clock you asked for.
Duplicate addresses are dropped before anything renders, so a messy list does not cost twice.
📥 What you give it
{
"urls": ["https://example.com", "https://news.ycombinator.com"],
"fullPage": true,
"viewportWidth": 1440,
"format": "jpeg",
"quality": 80,
"removeSelectors": ["#cookie-banner", ".intercom-launcher"],
"maxItems": 10
}
| Field | Comes set to | What it is |
|---|---|---|
urls | two examples | The pages, one per line. A bare domain works. Up to 300. |
maxItems | 10 | Hard stop on how many pages this run photographs, 1 to 300. Keep it low while testing. |
fullPage | false | Off gives you exactly the viewport. On scrolls and stitches the whole page. |
viewportWidth, viewportHeight | 1440, 900 | The browser window. 390 wide is a phone, 1920 a desktop monitor. |
deviceScaleFactor | 1 | 2 is retina, 3 is print. Remember 2x is four times the file size. |
format, quality | jpeg, 80 | JPEG is much smaller. PNG is lossless and can carry transparency. Quality applies to JPEG only. |
darkMode, mobile | false | Ask the page for its dark theme, and render as a touch phone. Pair mobile with a narrow width. |
waitUntil | domcontentloaded | How ready the page must be: commit, domcontentloaded, load or networkidle. On an ad-funded page networkidle often waits for the timeout. |
delayMs | 800 | Extra pause for animations and fonts, up to 15 seconds. |
navigationTimeoutMs | 25000 | Give up on a page that has not become ready in this long. If enough of the page exists by then, it is still captured. |
scrollToBottom | false | Scroll first so lazy images load. On automatically with fullPage. |
maxFullPageHeight | 8000 | Tallest a full-page shot may be before it is cut, up to 20,000 px. |
maxTotalImageMb | 250 | The run stops once it has written this many megabytes. Pages it never reached are not charged. |
selector | empty | A CSS selector to photograph one element instead of the page. |
hideSelectors, removeSelectors | empty | Up to 30 selectors each: hide keeps the space, remove takes it out of the layout. |
customCss | empty | Raw CSS injected just before the shot, up to 4,000 characters. |
transparentBackground | false | PNG only. Leaves the page background out, for dropping an element into a design. |
locale, timezone | empty | en-US, de-DE, Europe/Berlin, America/New_York. |
concurrency | 3 | Pages rendered at once, 1 to 6. Drop to 1 for a site that objects. |
useProxy | false | Turn it on if a site refuses the run. |
proxyUrls | empty | Your own servers, as http://user:pass@host:port. Only the first entry in the list is used, and it takes precedence over everything else. |
Leave urls empty and you get one sample row showing the shape, with no browser started and no screenshots charged.
📤 What you get back
A real row from a real run:
{
"ok": true,
"charged": true,
"recordType": "screenshot",
"url": "https://example.com/",
"screenshotUrl": "https://api.apify.com/v2/key-value-stores/zBdffYZdgUJdUcRAq/records/screenshot-001-example.com.jpg?signature=9w6b5...",
"screenshotKey": "screenshot-001-example.com.jpg",
"finalUrl": "https://example.com/",
"title": "Example Domain",
"statusCode": 200,
"imageWidth": 1440,
"imageHeight": 900,
"format": "jpeg",
"fullPage": false,
"darkMode": false,
"deviceScaleFactor": 1,
"viewportWidth": 1440,
"viewportHeight": 900,
"pageHeight": 900,
"truncated": false,
"fileSizeBytes": 17536,
"loadTimeMs": 1090,
"scrapedAt": "2026-09-21T01:27:34.234Z"
}
| Field | What it is |
|---|---|
screenshotUrl, screenshotKey | A direct link to the image, and its name in the run's key-value store. Download what you want to keep. |
url, finalUrl | What you asked for, and where it ended up after redirects. |
statusCode | The main document's status. A page that rendered on a 404 says 404 here. |
imageWidth, imageHeight | Read from the image itself, so this is the real picture size, density included. |
pageHeight, truncated | How tall the document was, and whether the height ceiling cut it. |
fileSizeBytes, loadTimeMs | Useful for spotting the page that is quietly eating your size budget. |
format, fullPage, darkMode, deviceScaleFactor, viewportWidth, viewportHeight | The settings this shot was taken with, copied onto the row, so a mixed dataset stays readable. |
🧾 Reading the output
recordType and ok separate the three kinds of row. charged is the flag that marks a real screenshot apart from the sample and the diagnostics, rather than a receipt.
| Row | How to spot it | Charged |
|---|---|---|
| A screenshot | recordType is screenshot, ok is true | yes |
| The sample row | _sample is true | no |
| A diagnostic | _diagnostic is true, with an errorCode | no |
The table view has no errorCode column, so a failed row can look like a thin successful one. A BLOCKED row even carries an image. Filter on recordType when you are counting.
| Code | What it means |
|---|---|
BLOCKED | The site would not let the page render. The image of whatever was on screen is still delivered. |
NAV_TIMEOUT | Nothing usable had loaded inside navigationTimeoutMs. |
NAV_ABORTED | Not a page the browser can display. A PDF or other file lands here. |
DNS_FAILED, CONNECTION_REFUSED, TLS_FAILED | The address did not resolve, the server refused, or the certificate did not check out. |
SELECTOR_NOT_FOUND | Your selector matched nothing on the page. |
IMAGE_TOO_LARGE | The picture went past the encoder's ceiling of 16,000 pixels a side. |
BUDGET_REACHED | maxTotalImageMb is spent. One row says so and the pages after it are skipped. |
TIME_BUDGET | The run ran out of time before reaching that page. |
RENDER_FAILED, RENDERER_CRASHED | The browser failed on that page. Worth one re-run. |
PROXY_INPUT_ADJUSTED | A network option you set was not one this actor uses, so it carried on without it. |
▶️ How to run it
1. Open Website Screenshot Generator and click Try for free. 2. Put your addresses into Page addresses, one per line. 3. Set Maximum screenshots low for the first run, so you can check the framing before paying for 300 of them. 4. Add the cookie banner and chat bubble to Remove these elements. That one change improves more screenshots than any other setting here. 5. Click Start, then open the rows to see the images, or pull them from the key-value store.
💰 How much does it cost?
$3.00 per 1,000 screenshots, which is $0.003 each. Flat on every Apify plan, no volume tiers.
You pay per image taken. The sample row is not charged, nor are the diagnostics: a page that was refused, timed out with nothing to show, had no matching selector, or was skipped once the size budget ran out. A page that rendered is charged whatever its status code says.
Ten screenshots is three cents. The whole 300 is ninety.
💡 What people use it for
- Thumbnails for a directory or a link roundup, generated from the list you already have.
- Before and after shots of a redesign, same width, same settings, two runs.
- Visual regression checks on a schedule: same pages, same size, compare the images.
- Capturing one element cleanly, a pricing table or a chart, with a transparent background for a
deck.
- Checking how a set of pages looks on a phone, dark mode on, without touching a device.
- Evidence and archiving, with
finalUrl,statusCodeand a timestamp on every row.
🚧 What it does not do
- Nothing behind a login. No cookies, no sign-in, no sessions.
- 300 pages per run. Split a longer list, or schedule it.
- The page is frozen once it has painted, so content that arrives very late can be missing. Raise
delayMs or use load or networkidle when that matters.
- Height and size ceilings. 20,000 pixels of page by your setting, 16,000 pixels a side at the
encoder, and the total megabytes you allowed.
- No PDF output. PNG and JPEG only.
- No text extraction. For the words on the page, use the crawler linked below.
- It cannot make a site render. A page that refuses gives you the image of the refusal, free.
- Only the first entry in your own server list is used.
🧭 Which site tool do you need?
| If you want | Use |
|---|---|
| Pictures of pages | This one |
| The same pages as text, Markdown and chunks | Website Intelligence Crawler |
| Repositories and developer profiles | GitHub Scraper |
| Package metadata and download counts | npm + PyPI Package Scraper |
❓ Questions people ask
How do I get rid of the cookie banner? Put its CSS selector into Remove these elements. Open the site, inspect the banner, copy the id or class.
Why is my full-page shot cut off? It hit maxFullPageHeight. The row says truncated: true. Raise the ceiling, but remember an endless feed has no bottom.
Can I screenshot one element? Yes, with selector. Pair it with PNG and a transparent background to drop it straight into a design.
Where do the images live? In the run's key-value store, linked from screenshotUrl on every row. Download the ones you want to keep.
Why do two runs of the same page look different? Fonts, animations and ads settle at different speeds. Raise delayMs, or switch waitUntil to load, and the shots get more repeatable.
Is screenshotting a public page legal? It photographs public pages as a visitor sees them. What you do with the images afterwards is subject to copyright and the site's terms. Apify's write-up on the legality of web scraping is a good starting point, and we are not lawyers.
🆘 If something breaks
Open the Issues tab on the actor page. Send the address and the run ID. If a diagnostic row landed, its errorCode and details usually name the reason already.