# Website Screenshot – Full Page PNG/JPEG, CJK & Emoji Fonts (`raspberry_rondel/website-screenshot-pro`) Actor

Full-page or viewport screenshots as PNG/JPEG, custom width/height, with proper Chinese, Japanese, Korean & emoji fonts. Built for heavy pages, clear status per URL. No proxies: sites that block cloud IPs are marked failed and not charged.

- **URL**: https://apify.com/raspberry_rondel/website-screenshot-pro.md
- **Developed by:** [星賢 陳](https://apify.com/raspberry_rondel) (community)
- **Categories:** Developer tools, Automation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/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: Full-Page PNG/JPEG with Chinese, Japanese, Korean and Emoji Fonts

Capture screenshots of a list of web pages for website monitoring, QA and visual testing, archiving or internal reference. You can capture the **full page** or **just the viewport**, at the **width and height** you choose (within the limits below), as **PNG or JPEG**. Every image is saved in the run's key-value store, and the dataset gets one row per URL with a **direct link to the image**, the final URL, the page title and the HTTP status.

Chinese (Traditional and Simplified), Japanese and Korean pages render with Noto CJK fonts, not empty boxes ("tofu"), and emoji render in colour. The Actor respects each site's `robots.txt` by default.

### Features

- **Full page or viewport.** With `fullPage: true`, the Actor scrolls first so lazy-loaded images appear, then captures the whole page up to `maxHeightPx` (default 16,000 px). Longer pages are cut at that height and flagged with `truncatedToMaxHeight`. The image stays `viewportWidth` wide, including on pages with horizontal overflow.
- **Custom viewport.** `viewportWidth` (320–3840) and `viewportHeight` (240–2160), plus `deviceScaleFactor` 1–3 for retina images.
- **PNG or JPEG**, with adjustable `jpegQuality`.
- **Wait options:**

  - `waitUntil`: `load` (default), `domcontentloaded` or `networkidle`;
  - an extra `delayMs` after loading;
  - `waitForSelector` waits until a specific element appears.

  The Actor also waits for web fonts to finish loading.
- **Clean shots.** Hide cookie banners and chat widgets with `hideSelectors` (CSS selectors). Use it for cookie banners and chat widgets only. Do not use it to hide paywalls, registration walls, or copyright and attribution notices. Scrollbars are hidden.
- **Locale and time zone.** For example `zh-TW` and `Asia/Taipei`, so the site serves the language and dates you need.
- **Built for real-world sites.**
  - Each URL gets a fresh, isolated browser context.
  - Navigation is fault-tolerant: if the `load` event does not fire because of a slow tracker or a stuck video, the Actor still captures what has rendered and adds a note.
  - Failed pages are retried with a fresh context (`maxRetries`), and every URL has an overall time budget.
  - A crashed browser is restarted automatically.
- **Polite by design.**
  - `robots.txt` is checked before each page and for every redirect hop, using the rules for `RondelBot` (or for `*` if there is no `RondelBot` group). If a site's robots.txt cannot be fetched because of a server error or timeout, the site is treated as disallowed.
  - `respectRobotsTxt` is on by default. Turning it off is your decision and your responsibility; do it only for sites you own or have written permission to capture.
  - At least 1 second between page loads on the same site; a longer robots.txt `Crawl-delay` is honoured while `respectRobotsTxt` is on.
  - The browser identifies itself with a `RondelBot` token in its user agent.
  - Anti-bot challenges are reported, not bypassed.
  - Site owners can opt out with `User-agent: RondelBot` / `Disallow: /` in robots.txt.
- **Network safety.** Page URLs that point to private or local network addresses are refused.

### Output

One dataset row per URL:

```json
{
  "url": "https://www.python.org/",
  "finalUrl": "https://www.python.org/",
  "status": "ok",
  "httpStatus": 200,
  "title": "Welcome to Python.org",
  "screenshotKey": "0012-www-python-org-1ab11d99.png",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<store>/records/0012-www-python-org-1ab11d99.png",
  "format": "png", "fullPage": true,
  "viewportWidth": 1280, "viewportHeight": 800,
  "imageWidth": 1280, "imageHeight": 2525,
  "truncatedToMaxHeight": false,
  "bytes": 477812,
  "notes": [],
  "error": null,
  "charged": true,
  "seconds": 17.4,
  "takenAt": "2026-10-17T07:51:24+00:00"
}
```

`status` is one of:

- `ok`: the screenshot was taken;
- `ok-http-error`: the page answered 4xx/5xx; the error page is still captured so you can see it, and it is free;
- `blocked-by-robots-txt`;
- `blocked-by-anti-bot`;
- `invalid-url`;
- `unreachable`: the domain does not exist or could not be resolved;
- `error`;
- `skipped-spending-limit`;
- `duplicate`: the same page appeared earlier in your list (see below); the row has `duplicateOf` and is free.

**Duplicate URLs.** Before anything is loaded or charged, URLs are normalised for duplicate detection: surrounding spaces trimmed, scheme and host lower-cased, the `#fragment`, `utm_*` and common click-tracking parameters (such as `fbclid` and `gclid`) removed, default ports dropped, and `/path` and `/path/` treated as the same page. A URL that matches an earlier one gets status `duplicate` with `duplicateOf` naming the first occurrence, is not loaded and is not charged. The first occurrence is captured from the URL as you entered it. `http://` and `https://` versions of a page are not treated as duplicates.

The **Overview** dataset view shows the screenshots as images. Files can also be downloaded from the run's key-value store (Storage tab) or through the API.

### How to use

1. Add your pages to **URLs**, one per line.
2. Choose full page or viewport, the size and the format.
3. Run it. Each row's `screenshotUrl` opens the image.

Examples:

- Mobile-width full page: `{"urls": ["https://www.ikea.com.tw/zh"], "viewportWidth": 390, "viewportHeight": 844, "deviceScaleFactor": 2}`
- Small JPEG thumbnails of the first screen only: `{"urls": [...], "fullPage": false, "format": "jpeg", "jpegQuality": 70}`
- Taiwanese locale: `{"urls": [...], "locale": "zh-TW", "timezoneId": "Asia/Taipei"}`

### Some websites block cloud servers: what happens then

Be aware of this before you start:

- **Some websites refuse visitors coming from cloud data centres**, which is where Apify runs. Large corporate, government and news sites are the usual examples.
- **This Actor uses no proxies and no disguise.** It visits from Apify's own servers, tells the site who it is through its user agent (`RondelBot`), and does not try to get past anti-bot checks.
- When a site refuses, the URL is **marked as failed with a clear reason and is NOT charged**:

  - `blocked-by-anti-bot` for a challenge page;
  - `ok-http-error` for a 403 or other error page, which is still captured so you can see what the site returned;
  - `error` with the browser's message for a dropped connection;
  - `blocked-by-robots-txt` when the site's robots.txt disallows the page.

  Every row has `charged: true` or `charged: false`, so you can see exactly what you paid for.

In an internal test (2026-10-17, 30 popular Taiwanese and international sites), 4 sites refused cloud visitors. All 4 were reported this way and none were charged.

### Pricing

Pay per event. **You pay only for screenshots of pages that loaded with an HTTP status below 400:**

| Event | Price |
|---|---|
| Screenshot (`screenshot`) | **$0.004** per screenshot stored ($4 per 1,000) |
| Actor start (`apify-actor-start`) | $0.0005 per GB of run memory, charged once per run ($0.002 at the default 4 GB). Put many URLs in one run to spread it. |

**Free (not charged):**

- pages blocked by the site (anti-bot, cloud-IP blocks, dropped connections) or by robots.txt;
- pages that answer with an HTTP error (the error page is still captured);
- invalid URLs, unreachable domains, failed loads and duplicate URLs (`duplicate`);
- URLs skipped because of your spending limit.

#### What the price includes

- **Full-page capture built for heavy pages.** The Actor scrolls so lazy images load, captures up to 16,000 px by default (30,000 px maximum), and keeps the image at the width you asked for. In an internal test (2026-10-17, 30 sites), all 26 sites that served their page produced a correct screenshot. These included Stripe, Apple, GitHub, BBC and a Wikipedia article 75,481 px tall (captured up to the height limit), with no crashes or timeouts in that test. Results on other sites may differ.
- **Your viewport, your format.** Width 320–3840 and height 240–2160, 1–3× retina, and PNG or JPEG with adjustable quality.
- **Chinese, Japanese, Korean and emoji fonts.** Noto CJK and Noto Color Emoji fonts are built in to avoid empty "tofu" boxes on Asian-language pages.
- **Stability features.** Each URL gets a fresh, isolated browser, with a retry, a time budget per URL and automatic browser restart. If a page's `load` event does not fire, the Actor captures what has rendered instead of failing.
- **Failures are free, and every URL gets a transparent status:** HTTP status, final URL, title, notes such as "page cut at maxHeightPx", and whether it was charged.
- **Spending limit.** Set **Maximum cost per run** and the Actor stops before going over it. If a run is restarted, URLs already in the dataset are not captured or charged again.

Memory: the default 4 GB processes about 6 pages in parallel (the Actor chooses about one page per 680 MB unless you set `maxConcurrency`). 2 GB works for a few URLs, and more memory finishes large batches faster.

### Limitations

- **Logins, paywalls and anti-bot walls.** The Actor has no login, cookie, header or proxy options and does not try to get past logins, paywalls or anti-bot walls. Such pages usually fail (reported and free). If the site instead serves a normal page, such as its login form or paywall notice, that page is captured as an anonymous visitor sees it and is charged like a normal page.
- Sites that block cloud IPs cannot be captured (reported and free).
- Very tall pages are cut at `maxHeightPx` (maximum 30,000 px).
- Cookie and consent banners are shown unless you list them in `hideSelectors`.
- Videos and animations are captured as a single still frame.
- Heavy pages can take a long time each. In an internal stress test (2026-10-17, 200 URLs at the default 4 GB), the whole run took 1,244 seconds. You pay per screenshot, not per second.

### Responsible use and legal notice

This Actor only visits the URLs you give it, from Apify's servers, with an honest user agent (`RondelBot`). By default it obeys robots.txt (including redirects) and waits at least 1 second between page loads on the same site. It has **no login, cookie, header or proxy options** and does not try to get past logins, paywalls, CAPTCHAs or other anti-bot protections.

**You are responsible for how you use it and for what you do with the results.** Before running it, make sure you have the right to access and copy the pages you submit, and that your use complies with the websites' terms, copyright law and data-protection law (for example GDPR, CCPA, or Taiwan's Personal Data Protection Act). Turning off `respectRobotsTxt` is your decision and your responsibility; do it only for sites you own or have written permission to access.

Results can contain personal data (for example names or photos shown on a page). Do not collect personal data unless you have a lawful basis to do so. Your inputs and results are stored only in your own Apify account (the run's dataset and key-value store); the Actor does not send them to the developer or to third parties.

**Screenshots are copies.** A screenshot reproduces the page's text, images and design, which may be protected by copyright. Keep screenshots for purposes such as monitoring, testing, archiving or internal reference, and do not republish them without permission. Use `hideSelectors` only for cookie banners and chat widgets, not to hide paywalls, registration walls or copyright and attribution notices.

Site owners can opt out by adding `User-agent: RondelBot` and `Disallow: /` to their robots.txt; the change applies from the Actor's next visit to the site.

This notice is not legal advice. If you are unsure whether your use is lawful, consult a lawyer.

### 負責任使用與法律聲明

本 Actor 只會從 Apify 伺服器造訪您提供的網址，並以誠實的使用者代理名稱（`RondelBot`）表明身分。預設遵守 robots.txt（含轉址），同一網站的頁面載入之間至少間隔 1 秒。本 Actor **不提供登入、cookie、自訂標頭或 proxy 功能**，也不會嘗試繞過登入、付費牆、驗證碼或其他防爬機制。

**使用方式與結果的用途由您自行負責。** 執行前，請確認您有權存取及複製所提交的網頁，且您的使用符合各網站的使用條款、著作權法與個人資料保護法規（例如 GDPR、CCPA 或我國《個人資料保護法》）。關閉 `respectRobotsTxt` 是您的決定與責任，請僅用於您擁有或已取得書面許可的網站。

結果可能含有個人資料（例如網頁上的姓名或照片）。除非您有合法依據，請勿蒐集個人資料。您的輸入與結果只存放在您自己的 Apify 帳號中（該次執行的 dataset 與 key-value store），本 Actor 不會將其傳送給開發者或第三方。

**截圖即重製。** 截圖會重製網頁上的文字、圖片與設計，這些可能受著作權保護。請將截圖用於監控、測試、存檔或內部參考等用途，未經許可請勿公開轉載。`hideSelectors` 僅用於隱藏 cookie 橫幅或客服視窗，不得用來隱藏付費牆、註冊牆或著作權與出處標示。

網站管理者可在 robots.txt 加入 `User-agent: RondelBot` 與 `Disallow: /` 拒絕本 Actor 造訪；自本 Actor 下次造訪該網站起生效。

本聲明並非法律意見；如不確定您的用途是否合法，請諮詢律師。

# Actor input Schema

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

Pages to capture (http/https). Duplicates are removed before anything is fetched or charged, ignoring #fragment, tracking parameters (utm\_\*, fbclid, gclid …), host letter case, default port and a trailing slash; each duplicate gets a free row with status duplicate.

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

Capture the whole scrollable page (up to maxHeightPx). Off = only the viewport.

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

PNG (lossless) or JPEG (smaller files).

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

1-100, only for JPEG.

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

Browser window width. 1280 = laptop, 390 = phone.

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

Browser window height (= image height when Full page is off).

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

1 = normal, 2 = retina (4x pixels, bigger files).

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

Page load event to wait for before capturing. If it is not reached within the timeout, whatever has rendered is captured and a note is added.

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

Wait this long after loading (and scrolling) before capturing, e.g. for animations.

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

Optional. Wait until this element appears (max 30 s) before capturing.

## `scrollToBottom` (type: `boolean`):

Scroll through the page first so lazy-loaded images appear (max 10 s), then back to the top.

## `maxHeightPx` (type: `integer`):

Very long pages are cut at this height (noted in the output).

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

Elements to hide before capturing, e.g. a cookie banner or chat widget: #cookie-banner, .intercom-launcher

## `respectRobotsTxt` (type: `boolean`):

On (default): pages disallowed for bots by the site's robots.txt (also after redirects) are skipped and not charged, and Crawl-delay is honoured between pages of the same site.

## `navigationTimeoutSecs` (type: `integer`):

Hard limit for the page to start rendering (DOMContentLoaded).

## `maxRetries` (type: `integer`):

Retries per URL after a navigation error (fresh browser context each time).

## `locale` (type: `string`):

e.g. en-US, zh-TW, ja-JP. Sets navigator.language and Accept-Language.

## `timezoneId` (type: `string`):

Optional IANA time zone, e.g. Asia/Taipei.

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

How many pages are captured at the same time. Leave empty to choose automatically from the run memory (about 1 page per 680 MB: 6 at the default 4 GB). Lower it for very heavy pages.

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org",
    "https://zh.wikipedia.org/wiki/臺北市"
  ],
  "fullPage": true,
  "format": "png",
  "jpegQuality": 80,
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "deviceScaleFactor": 1,
  "waitUntil": "load",
  "delayMs": 500,
  "scrollToBottom": true,
  "maxHeightPx": 16000,
  "respectRobotsTxt": true,
  "navigationTimeoutSecs": 45,
  "maxRetries": 1,
  "locale": "en-US"
}
```

# Actor output Schema

## `screenshots` (type: `string`):

No description

## `files` (type: `string`):

No description

## `runSummary` (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.python.org",
        "https://zh.wikipedia.org/wiki/臺北市"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("raspberry_rondel/website-screenshot-pro").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.python.org",
        "https://zh.wikipedia.org/wiki/臺北市",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("raspberry_rondel/website-screenshot-pro").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.python.org",
    "https://zh.wikipedia.org/wiki/臺北市"
  ]
}' |
apify call raspberry_rondel/website-screenshot-pro --silent --output-dataset

```

## MCP server setup

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

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/8vdnDvJqKXHusSGYb/builds/4bAULNVRw8p0SK3O4/openapi.json
