# Fast Website Screenshot — full-page, pay only on success (`gratified_ashram/fast-screenshot`) Actor

Full-page website screenshots that actually capture the full page. Fails fast instead of hanging, and only charges for screenshots that succeed.

- **URL**: https://apify.com/gratified\_ashram/fast-screenshot.md
- **Developed by:** [Tit Slobodjanac](https://apify.com/gratified_ashram) (community)
- **Categories:** SEO tools, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 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.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Fast Website Screenshot

Full-page website screenshots that capture the **whole** page — fast, and you only pay for screenshots that actually come back.

Give it a URL, get a PNG, JPEG, WebP or PDF. No API key, no browser infrastructure to run.

***

### Why this one

Most screenshot tools fail in the same four ways. This one is built around not doing that.

**The full page really is the full page.** Long pages are scrolled first so lazy-loaded images render, and sticky headers are pinned in place so your navigation bar isn't stamped down the image every 900 pixels. "Full page" that silently stops at the fold is the single most common complaint about screenshot tools, and it's the first thing this fixes.

**It's quick, and the numbers below are real.** Most pages finish in a couple of seconds. Very tall pages take longer, because time is dominated by encoding the image, and that scales with pixel count — so the honest range is seconds for ordinary pages, tens of seconds for the extremes. Measured on the Apify platform at default settings:

| Page | Page height | Captured | Time |
|---|---|---|---|
| news.ycombinator.com | 1,185 px | 1,185 px | 1.3 s |
| apify.com | 9,578 px | 9,578 px | 4.4 s |
| stripe.com | 14,759 px | 14,759 px | 11–27 s |
| en.wikipedia.org/wiki/Slovenia | 48,273 px | 20,000 px (capped) | 17.6 s |

Trackers, video and audio are skipped to save time. Images and web fonts are always loaded — blocking fonts would be faster still, but the screenshot would come back in fallback typefaces and no longer match the real page.

**It fails fast, and failures are free.** The per-page timeout (60 s by default) is a budget for the *whole* page, not for each internal step, so a slow page cannot quietly run several times longer than the limit you set. If a page won't render, the run moves on instead of hanging — and **you are not charged for a screenshot that didn't happen.** Every failed URL is written to the dataset with the reason.

**Very long pages are capped, and the output says so.** Encoding an unbounded capture of a 48,000 px article takes minutes, so captures stop at `maxPageHeight` (20,000 px by default, about 22 screens — set `0` to disable). Every record reports `pageHeight`, `capturedHeight` and `clipped`, read back from the encoded image itself, so you always know whether you got the whole page.

**Cookie banners are out of the way.** Common consent notices are hidden before capture. Note that they are *hidden, never accepted* — no consent is given on your behalf.

***

### Input

```json
{
  "urls": ["https://apify.com", "https://news.ycombinator.com"],
  "format": "png",
  "fullPage": true,
  "viewportWidth": 1440,
  "deviceScaleFactor": 2,
  "hideCookieBanners": true
}
```

| Option | Default | What it does |
|---|---|---|
| `urls` | — | One or more URLs to capture |
| `format` | `png` | `png`, `jpeg`, `webp` or `pdf` |
| `fullPage` | `true` | Whole scrollable page vs. just the viewport |
| `viewportWidth` / `viewportHeight` | 1440 × 900 | Browser window size |
| `deviceScaleFactor` | 1 | Set to `2` for retina-resolution output |
| `hideCookieBanners` | `true` | Hide common consent notices |
| `freezeStickyElements` | `true` | Stop fixed headers repeating down the image |
| `scrollToLoadLazyImages` | `true` | Scroll first so lazy images load |
| `hideSelectors` | `[]` | Extra CSS selectors to remove (chat widgets, popups) |
| `waitForSelector` | — | Wait for a specific element before capturing |
| `waitUntil` | `load` | `domcontentloaded` (fastest) → `networkidle` (slowest) |
| `delayMs` | 0 | Extra pause for animations |
| `timeoutSecs` | 60 | Hard budget for the whole page, not per step |
| `maxPageHeight` | 20000 | Cap for extremely long pages; `0` for no limit |
| `blockResources` | `true` | Skip trackers, video and audio (images and fonts always load) |
| `concurrency` | 4 | Pages captured in parallel |
| `proxyConfiguration` | — | Optional, for geo-specific rendering |

### Output

Images land in the key-value store. The dataset gets one row per URL:

```json
{
  "url": "https://apify.com",
  "success": true,
  "key": "screenshot-0001.png",
  "imageUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/screenshot-0001.png",
  "bytes": 835410,
  "durationMs": 4353,
  "title": "Apify: The largest marketplace of trusted tools for AI",
  "httpStatus": 200,
  "pageWidth": 1454,
  "pageHeight": 9578,
  "capturedHeight": 9578,
  "clipped": false,
  "timings": { "goto": 968, "scroll": 1095, "freezeSticky": 43, "fonts": 38, "render": 1357 }
}
```

`capturedHeight` is read back out of the encoded image rather than copied from what was requested, so `clipped: false` is a measurement, not a promise. `timings` breaks the duration down by phase, which makes a slow page diagnosable instead of mysterious.

Failures appear in the same dataset with `"success": false` and an `error` field, so a partial run is still useful.

### Good for

- Visual change monitoring — schedule it and diff the images
- Link previews and Open Graph thumbnails
- Archiving pages as PDF
- Design and competitor reference shots
- QA across viewport sizes

### Run locally

```bash
npm install
PW_CHROME=/usr/bin/google-chrome-stable node test/local-run.js https://example.com
```

Deploy with the Apify CLI:

```bash
npx apify-cli login
npx apify-cli push
```

### Pricing

**$5.00 per 1,000 screenshots** — half a cent each, charged only for screenshots that actually come back. A URL that fails costs you nothing, and platform usage is included, so the price you see is the price you pay.

There is one exception worth stating plainly rather than hiding: starting a run costs **$0.00001**, one thousandth of a cent, which works out to a single cent per thousand runs. It exists because defining that event waives the first five seconds of container startup, and that saving is what keeps the per-screenshot price this low.

# Actor input Schema

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

One or more page URLs to capture.

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

PNG for quality, JPEG/WebP for smaller files, PDF for print-style output.

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

Capture the entire scrollable page, not just what fits above the fold.

## `maxPageHeight` (type: `integer`):

Safety cap for extremely long pages, about 22 screens by default. Image encoding time grows with pixel count, so an unbounded capture of a very long article can take minutes. Set to 0 for no limit. Output reports capturedHeight and clipped so you always know whether the image is the whole page.

## `viewportWidth` (type: `integer`):

Browser window width. 1440 matches a typical desktop; use 390 for a phone-sized layout.

## `viewportHeight` (type: `integer`):

Browser window height. Ignored when Full page is on, except to decide how much loads first.

## `deviceScaleFactor` (type: `integer`):

Set to 2 for retina-resolution output.

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

Hides common consent notices so they do not cover the page. Banners are hidden, never accepted — no consent is given on your behalf.

## `freezeStickyElements` (type: `boolean`):

Stops fixed navigation bars from being repeated down a full-page screenshot.

## `scrollToLoadLazyImages` (type: `boolean`):

Scrolls the page before capturing so lazy-loaded images are rendered.

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

Additional elements to remove before capture, e.g. chat widgets.

## `waitForSelector` (type: `string`):

Optional CSS selector to wait for before capturing.

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

How long to wait before capturing. Load event suits most sites; Network idle is safest for heavy single-page apps but slower.

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

Additional wait after the page settles, for animations.

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

Hard limit for the whole page, not per step. The page fails fast instead of hanging, and failures are never charged. Very long pages (a full Wikipedia article) need 45s or more when Full page is on.

## `blockResources` (type: `boolean`):

Skips analytics and tracking scripts, plus video and audio, to speed up capture. Images and web fonts are always loaded so the screenshot matches the real page.

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

Compression quality from 10 to 100. Only applies to JPEG and WebP output. 85 is a good size-to-quality balance.

## `colorScheme` (type: `string`):

Renders the page as a visitor with a light or dark system theme would see it.

## `concurrency` (type: `integer`):

How many pages to capture at the same time. Higher is faster for long URL lists; lower is gentler on the target site.

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

Optional. Useful for geo-specific rendering.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com"
  ],
  "format": "png",
  "fullPage": true,
  "maxPageHeight": 20000,
  "viewportWidth": 1440,
  "viewportHeight": 900,
  "deviceScaleFactor": 1,
  "hideCookieBanners": true,
  "freezeStickyElements": true,
  "scrollToLoadLazyImages": true,
  "hideSelectors": [],
  "waitUntil": "load",
  "delayMs": 0,
  "timeoutSecs": 60,
  "blockResources": true,
  "jpegQuality": 85,
  "colorScheme": "light",
  "concurrency": 4
}
```

# 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://apify.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gratified_ashram/fast-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": ["https://apify.com"] }

# Run the Actor and wait for it to finish
run = client.actor("gratified_ashram/fast-screenshot").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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://apify.com"
  ]
}' |
apify call gratified_ashram/fast-screenshot --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=gratified_ashram/fast-screenshot",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/6OwR3u72Tufa0ZBM0/builds/ceBwEz8PBZgWYz8De/openapi.json
