# Bulk Website Screenshots from CSV, Google Sheet or Dataset (`nerolabs/bulk-website-screenshots`) Actor

Screenshots every website in an Apify dataset, CSV, Excel file, Google Sheet or URL list, desktop and/or mobile, cookie banners hidden, adding a public image link to each row. Inputs: datasetId, fileUrl or urls. Charged per screenshot stored, never for dead or blocked sites. Agent-ready: x402, MCP.

- **URL**: https://apify.com/nerolabs/bulk-website-screenshots.md
- **Developed by:** [Adam Pearce](https://apify.com/nerolabs) (community)
- **Categories:** Lead generation, Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 screenshots

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bulk Website Screenshots from CSV, Google Sheet or Dataset

**You have a list of 500 businesses. You need a picture of every one of their websites.** For a personalised video, a web design pitch, an audit, or just to see which sites look like they were built in 2009. Opening them one by one and pressing screenshot takes an afternoon.

Give this Actor the list as it is: the Apify dataset from your Google Maps scrape, a CSV, an Excel file or a Google Sheet. It finds the website column on its own, opens every site in a real Chrome browser, hides the cookie banner, and takes a desktop screenshot, a mobile one, or both. **Every row of your list comes back with all its own columns plus a public image link,** so the business name, phone number and screenshot sit side by side, ready to use.

### What it does

- **Reads your list as it is**: an Apify dataset (any scraper's output), a public CSV, TSV, Excel, JSON or JSON Lines file, a Google Sheet link, or just websites pasted one per line.
- **Finds the website column for you**: `website`, `Website`, `url`, `domain` and friends are picked up automatically. `example.com`, `www.example.com` and full links all work.
- **Desktop, mobile or both**: desktop at 1366 x 768 (any size up to 4K), mobile at iPhone size with a phone browser, so you see what a customer on their phone sees.
- **Clean shots**: the consent pop-ups of the common cookie platforms (OneTrust, Cookiebot, Usercentrics, Complianz, CookieYes and more) are hidden, so the website itself is visible. Nothing is accepted on the visitor's behalf.
- **Above the fold or full page**: the first screen a visitor sees (the default, and what outreach videos use), or the whole page down to the footer.
- **Public image links**: every screenshot gets a link that opens in any browser, Dubb, Loom, Sendspark, Canva or a spreadsheet, no Apify login needed.
- **Honest about every row**: each row says `ok`, `no_website`, `invalid_url`, `unreachable`, `timeout`, `site_error` (the site answered 403, 404 or 500), `private_address`, `partial` or `repeat`, with a plain-English note.
- **Optional CSV or Excel export** of the whole list with the links added.

### Example

Your Google Maps scrape, or a sheet like this:

| name | city | Website |
|---|---|---|
| Greggs | Newcastle | greggs.co.uk |
| Screwfix | Yeovil | www.screwfix.com |

comes back as:

| name | city | Website | screenshotStatus | screenshotUrl | mobileScreenshotUrl | pageTitle |
|---|---|---|---|---|---|---|
| Greggs | Newcastle | greggs.co.uk | ok | https://api.apify.com/.../screenshot-000001-desktop.jpg | https://api.apify.com/.../screenshot-000001-mobile.jpg | Greggs |
| Screwfix | Yeovil | www.screwfix.com | ok | https://api.apify.com/.../screenshot-000002-desktop.jpg | https://api.apify.com/.../screenshot-000002-mobile.jpg | Screwfix |

The Console's Screenshots view shows the images as thumbnails right in the table.

### What it is good for

- **Personalised video outreach**: a screenshot of the prospect's own website as the background of every Dubb, Loom or Sendspark video, for a whole lead list in one run.
- **Web design and SEO agencies**: see at a glance which businesses on a list have an outdated, broken or mobile-unfriendly site, before you pitch.
- **Lead qualification**: run it straight after a Google Maps or directory scrape and sort the list by what the websites look like.
- **Audits, reports and archives**: a dated picture of every site in a portfolio, a competitor set or a client list.

### Pricing

Pay per event, no subscription:

| Event | Price |
|---|---|
| Screenshot stored (desktop and mobile each count once) | $0.004 (Bronze $0.0036, Silver $0.0032, Gold $0.0028) |
| Export file (CSV or Excel, only if you ask) | $0.01 |
| Actor start, per GB of memory | $0.001 (the default 2 GB run is $0.002) |

**Never charged**: rows with no website, values that are not web addresses, sites that do not exist or do not answer, sites that answer with an error page (403, 404, 500), private network addresses, and repeats of a website already captured in the same run.

Worked examples: 500 leads on desktop only is about **$2.00**. The same 500 on desktop and mobile is about **$4.00**. A 50-row test list is about $0.20.

### Notes worth knowing before your first big run

- **Big national chains with heavy bot protection** (some supermarkets, banks, big retailers, some universities) refuse cloud servers outright. Those rows come back as `site_error` with the HTTP code and cost nothing. Ordinary business websites, the kind on a local lead list, load normally.
- **Speed**: about 4 websites at a time at the default 2 GB, roughly 40 websites on both devices in under 5 minutes. Give the run more memory to go faster; the price per screenshot does not change.
- **Facebook or Google Maps links in the website column** are captured as given, with a warning, because a profile page is not the business's own website.
- **Very long pages** in full-page mode are cut at 12,000 pixels tall and flagged `fullPageTruncated`.
- **How long the images last**: screenshots live in the run's storage for as long as your Apify plan keeps it (7 days on the Free plan). Download them or chain [Bulk Image & File Downloader](https://apify.com/nerolabs/bulk-file-downloader) to keep them.
- **Proxy**: off by default. Apify datacenter proxy or your own proxy URLs can be switched on. Residential proxy is not supported, because one page through it costs more than the screenshot price.

### FAQ

**Can I feed it my Google Maps scraper results directly?** Yes. Pick the run's dataset in the 'Dataset' field. The `website` column is found automatically and every other column (name, phone, address, rating) comes back untouched.

**Does it click "Accept cookies"?** No. It hides the banners with styling, so nothing is consented to on anyone's behalf.

**Why did a site come back as `site_error`?** The site answered with an error code (shown in `httpStatus`): usually 403, meaning it blocks cloud servers, or 404, meaning the page no longer exists. You are not charged for it.

**Can I get PNG instead of JPEG?** Yes, set 'Image format' to PNG. JPEG is the default because the files are about five times smaller and look the same in a video or a slide.

**Is this legal?** It opens public web pages the way a visitor would and takes a picture of what is shown. What you then do with the pictures (and with any personal details in your own list) is your responsibility.

### The rest of the toolkit

Sibling Actors that fit together as a pipeline:

- [Dataset Filter & Transform](https://apify.com/nerolabs/dataset-filter-transform) keep only the leads you want before screenshotting them
- [Website Contact Finder](https://apify.com/nerolabs/website-contact-finder) business emails, phones and socials for the same list
- [Bulk PageSpeed & Lighthouse Checker](https://apify.com/nerolabs/bulk-pagespeed-checker) speed and Core Web Vitals scores for every site
- [Tech Stack Detector](https://apify.com/nerolabs/tech-stack-detector) which website builder, CMS and tools each site runs on
- [Bulk Image & File Downloader](https://apify.com/nerolabs/bulk-file-downloader) all the screenshots in one ZIP
- [Actor Pipeline Runner](https://apify.com/nerolabs/actor-pipeline-runner) chain any of these in one call

### For AI agents

Pay per event, agent-payable through x402 and MCP, limited permissions, no standby. Inputs: `datasetId`, `fileUrl` (CSV, Excel, JSON, Google Sheet), `urls` (a plain list) or `data` (inline rows); `websiteField` (auto-detected when empty); `devices` (`desktop`, `mobile`); `fullPage`, `imageFormat`, `desktopWidth`, `desktopHeight`, `waitSecs`, `hideCookieBanners`; `maxItems`; `exportFormats`. Returns one dataset item per input row with every original column plus `screenshotStatus`, `screenshotUrl`, `mobileScreenshotUrl`, `finalUrl`, `pageTitle`, `httpStatus` and `screenshotError`, and a `SCREENSHOT_SUMMARY` key-value record. Charged per screenshot stored; failures, blocked sites and repeats are not charged.

### Notes

Screenshots are taken with [Playwright](https://playwright.dev) (Apache 2.0) and Chrome. The default input uses large, well-known public websites only.

If these screenshots saved you an afternoon of clicking, a review on the Apify Store helps a lot. Found a site it captured badly? Open an issue with the link and I will look at it.

# Actor input Schema

## `datasetId` (type: `string`):

Pick an Apify dataset, for example a Google Maps scraper run. Every row keeps all its columns and gets its screenshot links added. Use this OR 'File or Google Sheet URL' OR 'Website URLs' OR 'Rows'.

## `fileUrl` (type: `string`):

A public link to a CSV, TSV, Excel .xlsx, JSON or JSON Lines file, or a Google Sheet shared as 'anyone with the link can view'. The website column is found automatically.

## `sheetName` (type: `string`):

Which sheet of an Excel file to read. Leave empty for the first sheet.

## `fileFormat` (type: `string`):

Only needed if automatic detection reads the file wrongly.

## `urls` (type: `array`):

Or just paste websites, one per line. example.com and www.example.com work without https://.

## `data` (type: `array`):

Or paste rows as a JSON array, for example \[{"name": "Tesco", "website": "tesco.com"}].

## `websiteField` (type: `string`):

The column that holds each website, for example 'website', 'url' or 'domain'. Leave empty to detect it automatically. Dot paths such as 'business.website' work.

## `devices` (type: `array`):

Desktop, mobile, or both. Each device is its own screenshot and its own charge.

## `fullPage` (type: `boolean`):

Off: what a visitor sees first, above the fold, which is what outreach videos and audits usually use. On: the whole page down to the footer, cut at 12,000 pixels tall.

## `imageFormat` (type: `string`):

JPEG is about five times smaller and right for most uses. PNG keeps every pixel.

## `jpegQuality` (type: `integer`):

Only for JPEG. 80 looks clean at a fraction of the size.

## `desktopWidth` (type: `integer`):

1366 is the most common laptop screen. 1920 for a full HD view.

## `desktopHeight` (type: `integer`):

The visible height when 'Full page' is off.

## `hideCookieBanners` (type: `boolean`):

Hides the consent pop-ups of the common cookie platforms (OneTrust, Cookiebot, Usercentrics, Complianz and others) so the website itself is visible. Nothing is accepted on the visitor's behalf.

## `waitSecs` (type: `integer`):

After the page has loaded, wait this long for animations and lazy images. Raise it for slow, animated sites.

## `pageTimeoutSecs` (type: `integer`):

A website that has not loaded in this time is reported as 'timeout' and not charged.

## `maxItems` (type: `integer`):

Stop after this many rows, a cost guard for big lists. Hard ceiling 50,000 per run.

## `maxConcurrency` (type: `integer`):

Leave empty to pick from the run's memory (one per 512 MB, so 4 at the default 2 GB). Raise memory for faster runs; the price per screenshot stays the same.

## `exportFormats` (type: `array`):

Optional. Writes every row with its screenshot links to a CSV and/or Excel file with a public download link ($0.01 per file).

## `proxyConfiguration` (type: `object`):

Off by default: websites are opened directly. Turn on Apify Proxy (datacenter) or give your own proxy URLs for sites that block cloud servers. Residential proxy is not supported, because one page through it costs more than the screenshot price.

## Actor input object example

```json
{
  "fileFormat": "auto",
  "urls": [
    "https://www.apify.com",
    "https://www.bbc.co.uk",
    "https://www.screwfix.com"
  ],
  "devices": [
    "desktop"
  ],
  "fullPage": false,
  "imageFormat": "jpeg",
  "jpegQuality": 80,
  "desktopWidth": 1366,
  "desktopHeight": 768,
  "hideCookieBanners": true,
  "waitSecs": 2,
  "pageTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://www.apify.com",
        "https://www.bbc.co.uk",
        "https://www.screwfix.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nerolabs/bulk-website-screenshots").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": [
        "https://www.apify.com",
        "https://www.bbc.co.uk",
        "https://www.screwfix.com",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("nerolabs/bulk-website-screenshots").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://www.apify.com",
    "https://www.bbc.co.uk",
    "https://www.screwfix.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call nerolabs/bulk-website-screenshots --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nerolabs/bulk-website-screenshots"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7pgaH7J2vrc5CXGaV/builds/jsEaSLMgSdlktfMve/openapi.json
