# Website Screenshot Generator: Full Page, Mobile & Bulk URLs (`larkmoor/website-screenshot`) Actor

Take full-page or viewport screenshots of any list of URLs with a real Chrome browser: desktop, laptop, tablet and phone sizes, PNG or JPEG, cookie banners hidden, lazy images loaded, single-element capture, dark mode. Returns image links + status + title. $3 per 1,000 screenshots.

- **URL**: https://apify.com/larkmoor/website-screenshot.md
- **Developed by:** [Larkmoor Data](https://apify.com/larkmoor) (community)
- **Categories:** 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.55 / 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

## Website Screenshot Generator: Full Page, Mobile & Bulk URLs

Take screenshots of any list of web pages with a real Chrome browser — the whole scrollable page or just the first screen, on desktop, laptop, tablet or phone sizes — and get one row per screenshot with a direct image link, the final URL after redirects, the HTTP status code, the page title, the file size and the load time.

Cookie and consent pop-ups are hidden for you, lazy-loaded images are scrolled into view before a full-page shot, and you can capture a single element (a header, a pricing table, a chart) instead of the whole page.

### What you can use it for

- **Website monitoring and archiving**: a daily picture of your pages or a competitor's homepage, pricing page or landing pages
- **SEO and marketing reports**: above-the-fold and full-page shots of client sites on desktop and mobile in one run
- **QA and visual testing**: check how pages look on phone, tablet and desktop after every release
- **Lead lists and directories**: add a thumbnail of each company's website to your CRM or directory
- **Content and social**: crisp images of articles, dashboards or charts for slides and posts
- **Compliance evidence**: timestamped captures of ads, offers and terms as they were shown

### Why this one

- **$3 per 1,000 screenshots**, $0.001 per run start — pages that fail are listed with the error and **not charged**
- **Mobile done right**: the phone preset uses an iPhone size, touch, a mobile user agent and 2x pixel density, so you get the real mobile layout
- **Clean shots**: common consent banners (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, Osano and more) hidden automatically, plus your own CSS selectors
- **Complete full-page shots**: scrolls first so lazy images load
- **Bulk and parallel**: several pages at once, every URL × every device in one run; a page that crashes the browser is retried with a fresh browser instead of failing the run
- **Element capture, dark mode, retina, PNG or JPEG**, custom viewport sizes

### Input

| Field | What it does |
|---|---|
| `urls` | Pages to capture (bare domains get https://) |
| `devices` | Any of `desktop` (1366×768), `desktop-hd` (1920×1080), `laptop` (1280×800), `tablet`, `phone`, `custom` |
| `fullPage` | Whole page (default) or first screen only |
| `format` / `quality` | PNG or JPEG (with quality) |
| `hideCookieBanners` / `hideSelectors` | Hide consent pop-ups and any elements you name |
| `selector` | Capture one element only |
| `scrollToLoad` | Scroll first so lazy images load |
| `maxHeight` | Cut very long full-page shots at this height (default 10,000 px); `truncated` says when it happened |
| `waitUntil` / `delayMs` | When to shoot: `load`, `domcontentloaded` or `networkidle`, plus an extra delay |
| `darkMode` / `retina` | Dark color scheme / 2x desktop shots |
| `width` / `height` | Size for the `custom` device |
| `maxConcurrency` / `maxResults` | Parallel pages and run limit |

Example input:

```json
{
  "urls": [{"url": "https://apify.com"}, {"url": "news.ycombinator.com"}],
  "devices": ["desktop", "phone"],
  "fullPage": true,
  "format": "jpeg",
  "quality": 80
}
```

### Output

One row per screenshot:

```json
{
  "url": "https://news.ycombinator.com",
  "device": "phone",
  "width": 390,
  "height": 844,
  "fullPage": true,
  "format": "jpeg",
  "finalUrl": "https://news.ycombinator.com/",
  "statusCode": 200,
  "title": "Hacker News",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<store>/records/news-ycombinator-com-phone-1a2b3c4d5e6f.jpg?signature=…",
  "truncated": false,
  "bytes": 814649,
  "loadMs": 917,
  "error": null,
  "takenAt": "2026-10-23T10:38:02Z"
}
```

The images themselves are saved in the run's key-value store; `screenshotUrl` is a signed link that opens or downloads each one directly, no token needed. A page that could not be loaded comes back with `error` filled and `screenshotUrl` empty (not charged).

### How much does it cost?

Pay per event: **$0.003 per screenshot** ($3 per 1,000) and **$0.001 per run start** (per GB of memory). Compute is included.

- 50 competitor pages, desktop + phone, every morning (100 shots): about $0.30 a day
- 5,000 company homepages as thumbnails for a directory: about $15 once
- A weekly visual check of 200 pages on 3 devices (600 shots): about $1.80 a week

### FAQ

**Is it legal?**
It visits the public pages you list, like any browser, and takes a picture of what anyone would see. It does not log in or bypass access controls. You are responsible for how you use images of other people's websites (for example copyright and trademarks).

**Can it capture pages behind a login?**
No, only public pages.

**Why does a page look different from my browser?**
Some sites show regional content or A/B tests. Try `waitUntil: networkidle` or a `delayMs` for heavy apps, and `hideSelectors` for chat widgets or newsletter pop-ups.

**How long are the images kept?**
As long as the run's storage is kept on your account (Apify's data retention for your plan). Download them or send them on with an integration.

**Very long pages?**
Full-page shots of very long pages are big; use JPEG or capture the first screen only.

### Tips

- Use `phone` + `desktop` together for responsive checks.
- `selector: "#pricing"` captures just a pricing table for change tracking.
- Schedule the actor and connect Google Drive, Dropbox, Slack or a webhook to receive new screenshots automatically.

### Integrations

Works with Google Sheets, Google Drive, Slack, Zapier, Make, Airtable, webhooks, the Apify API and the Python and JavaScript clients. Dataset downloads as JSON, CSV, Excel, XML or HTML.

### Changelog

- **2026-10-23** — First release: bulk full-page and viewport screenshots, five device presets plus custom size, PNG/JPEG, consent-banner hiding, lazy-image scrolling, element capture, dark mode, retina, failed pages not charged.

# Actor input Schema

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

Pages to screenshot. A bare domain (example.com) becomes https://example.com.

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

One screenshot per URL per device.

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

Capture the whole scrollable page instead of only the first screen.

## `format` (type: `string`):

PNG for crisp text, JPEG for small files.

## `quality` (type: `integer`):

1–100, JPEG only.

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

Hide common cookie/consent pop-ups (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics and more).

## `hideSelectors` (type: `array`):

Extra elements to hide before the screenshot, e.g. '.newsletter-popup', '#chat-widget'.

## `selector` (type: `string`):

Screenshot just this element (e.g. 'header', '#pricing') instead of the page.

## `scrollToLoad` (type: `boolean`):

Scroll down the page first so lazy-loaded images appear in full-page shots.

## `maxHeight` (type: `integer`):

Full-page shots are cut at this height (CSS pixels) so endless pages and huge images don't fail. 0 = no limit.

## `waitUntil` (type: `string`):

When the page counts as loaded. 'networkidle' waits for all requests to settle (slower, best for heavy apps).

## `delayMs` (type: `integer`):

Wait this long after loading, e.g. for animations.

## `darkMode` (type: `boolean`):

Ask the page for its dark color scheme.

## `retina` (type: `boolean`):

Desktop shots at 2x pixel density (phones and tablets are always high-density).

## `width` (type: `integer`):

Viewport width for the 'custom' device.

## `height` (type: `integer`):

Viewport height for the 'custom' device.

## `timeoutSecs` (type: `integer`):

Give up on a page after this many seconds (one retry with a lighter wait).

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

Pages loaded at the same time (1–8). Raise memory if you raise this.

## `maxResults` (type: `integer`):

Upper limit for the run. Failed pages are listed with the error and not charged.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://apify.com"
    },
    {
      "url": "https://news.ycombinator.com"
    }
  ],
  "devices": [
    "desktop",
    "phone"
  ],
  "fullPage": true,
  "format": "png",
  "quality": 80,
  "hideCookieBanners": true,
  "scrollToLoad": true,
  "maxHeight": 10000,
  "waitUntil": "load",
  "delayMs": 0,
  "darkMode": false,
  "retina": false,
  "width": 1366,
  "height": 768,
  "timeoutSecs": 45,
  "maxConcurrency": 2,
  "maxResults": 1000
}
```

# Actor output Schema

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

All results as JSON items in the run's default dataset (also downloadable as CSV/Excel from the dataset).

# 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": [
        {
            "url": "https://apify.com"
        },
        {
            "url": "https://news.ycombinator.com"
        }
    ],
    "devices": [
        "desktop",
        "phone"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("larkmoor/website-screenshot").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": [
        { "url": "https://apify.com" },
        { "url": "https://news.ycombinator.com" },
    ],
    "devices": [
        "desktop",
        "phone",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("larkmoor/website-screenshot").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": [
    {
      "url": "https://apify.com"
    },
    {
      "url": "https://news.ycombinator.com"
    }
  ],
  "devices": [
    "desktop",
    "phone"
  ]
}' |
apify call larkmoor/website-screenshot --silent --output-dataset

```

## MCP server setup

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

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/uX90DfQPxOckAUhVN/builds/kxnaBY5iCOVbPSjDF/openapi.json
