# Web Evidence Pack – PDF, Screenshot, Text & Monitor (`mendelamavc/web-evidence-pack`) Actor

Capture public web pages into timestamped PDF, full-page screenshot, clean text, and HTML. Get metadata, SHA-256 fingerprints, and persistent change detection in one API-ready run for QA, compliance, audits, and monitoring.

- **URL**: https://apify.com/mendelamavc/web-evidence-pack.md
- **Developed by:** [Guilherme Mendel](https://apify.com/mendelamavc) (community)
- **Categories:** Developer tools, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $40.00 / 1,000 evidence packs

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## Web Evidence Pack

Capture public web pages as timestamped evidence bundles: PDF, full-page screenshot, clean text, source HTML, metadata, and a SHA-256 content fingerprint in one API-ready run.

The Actor is designed for QA checks, compliance records, public-page archiving, vendor-page audits, and scheduled change monitoring. It does not require a separate server, browser farm, or file host.

![Web Evidence Pack icon](./assets/web-evidence-pack-icon.png)

### What one successful event creates

- Printable A4 PDF with backgrounds
- Full-page PNG screenshot
- Extracted visible text
- Optional rendered HTML
- Final URL, HTTP status, page title, load time, timestamp, and word count
- SHA-256 fingerprint for deterministic change detection
- Persistent snapshots when a `snapshotKey` is supplied
- Dataset row plus downloadable files in the run's key-value store

The pay-per-event event is charged only after the page is successfully captured. Blocked and failed pages are not charged for an evidence-pack event.

### Example input

```json
{
  "urls": [
    { "url": "https://example.com" },
    { "url": "https://www.iana.org/help/example-domains" }
  ],
  "formats": ["pdf", "screenshot", "text", "html"],
  "respectRobotsTxt": true,
  "snapshotKey": "vendor-terms-weekly",
  "waitUntil": "domcontentloaded",
  "timeoutSecs": 30,
  "postLoadWaitSecs": 1,
  "maxConcurrency": 2
}
```

The `urls` field accepts URL strings through the API and request-list objects through the Console editor. A run can process up to 100 unique public URLs.

### Example result

```json
{
  "inputUrl": "https://example.com",
  "finalUrl": "https://example.com/",
  "status": "captured",
  "httpStatus": 200,
  "title": "Example Domain",
  "capturedAt": "2026-07-13T23:41:59.502Z",
  "loadTimeMs": 112,
  "wordCount": 19,
  "contentHash": "c1d5188c6a2eac7fe21f56b03a396b45cb434f30b653d6939f5d2db08babe99f",
  "changed": false,
  "firstCapture": true,
  "pdfRecordKey": "EVIDENCE_001_100680ad546c_PAGE.pdf",
  "screenshotRecordKey": "EVIDENCE_001_100680ad546c_FULL.png",
  "textRecordKey": "EVIDENCE_001_100680ad546c_TEXT.txt",
  "htmlRecordKey": "EVIDENCE_001_100680ad546c_SOURCE.html"
}
```

When the Actor runs on Apify, result rows also contain direct API URLs for generated artifacts.

### Change monitoring

Set a stable `snapshotKey`, then run the same input manually, through the API, or on an Apify schedule. The Actor stores the last SHA-256 hash for each input URL in a dedicated named key-value store.

- First capture: `firstCapture: true`, `changed: false`
- Same content later: `firstCapture: false`, `changed: false`
- Different rendered HTML: `firstCapture: false`, `changed: true`, with `previousHash`

Apify webhooks can notify another system after a scheduled run. Filter the dataset for `changed: true` in the receiving automation.

### Safety and responsible use

This Actor is intentionally limited to public HTTP(S) pages.

- Private, loopback, link-local, reserved, `.local`, and localhost targets are blocked.
- Redirects and browser subresources are checked to reduce SSRF risk.
- URLs containing embedded credentials are rejected.
- `robots.txt` is respected by default, and verification fails closed.
- Concurrency, timeouts, viewport size, input count, text size, and HTML size are bounded.
- Login automation, CAPTCHA bypass, private pages, and anti-bot evasion are not supported.

Use the Actor only for pages you may lawfully access and process. A timestamp and hash help establish a reproducible record, but they are not a qualified timestamp or a guarantee of legal admissibility.

### Limitations

- Browser-rendered HTML can include dynamic values, which may change the hash even when the meaningful content did not.
- Pages that require authentication, CAPTCHA solving, browser extensions, or disabled TLS verification are unsupported.
- Very large HTML is not stored above 15 MiB; visible text is truncated above 5 MiB.
- Full-page screenshots may fail on unusually tall or graphics-heavy pages. Other requested artifacts can still succeed and the result will include a warning.
- Artifact API URLs can require authorization when the storage is private.

### Local development

Requirements: Node.js 22 or newer.

```bash
npm install
npm run check
npm run test:integration
npx apify-cli validate-schema
npx apify-cli run --purge --input-file ./examples/input.json
```

The competitor monitor can be refreshed with:

```bash
npm run market:snapshot
```

It records adjacent Apify Store products, 30-day users, 30-day runs, and pricing models under `reports/snapshots/`.

### Outputs

- Default dataset: one structured result per input URL
- Default key-value store: PDF, PNG, TXT, HTML, and `OUTPUT` summary
- Named snapshot store: one `SNAPSHOTS` record when `snapshotKey` is supplied

### Support scope

Supported issues are reproducible failures on public pages within the documented limits. Custom login flows, anti-bot bypasses, legal certification, and custom scraping projects are outside this Actor's scope.

# Actor input Schema

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

Up to 100 public HTTP(S) pages. Login-protected pages, local networks and private IP addresses are not supported.

## `formats` (type: `array`):

Choose one or more artifacts to create for each page.

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

Fail closed when robots.txt cannot be verified. Keep enabled for public Store runs.

## `snapshotKey` (type: `string`):

Optional stable key used to compare this run with earlier captures.

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

DOM content loaded is the safest default for pages with continuous network activity.

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

Maximum navigation time for each page, from 5 to 90 seconds.

## `postLoadWaitSecs` (type: `integer`):

Optional pause for client-side rendering after navigation completes.

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

A conservative limit keeps memory use and target-site load under control.

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

Browser viewport width in CSS pixels.

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

Browser viewport height in CSS pixels.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://example.com"
    }
  ],
  "formats": [
    "pdf",
    "screenshot",
    "text"
  ],
  "respectRobotsTxt": true,
  "waitUntil": "domcontentloaded",
  "timeoutSecs": 30,
  "postLoadWaitSecs": 1,
  "maxConcurrency": 2,
  "viewportWidth": 1440,
  "viewportHeight": 900
}
```

# Actor output Schema

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

No description

## `artifacts` (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": [
        {
            "url": "https://example.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mendelamavc/web-evidence-pack").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://example.com" }] }

# Run the Actor and wait for it to finish
run = client.actor("mendelamavc/web-evidence-pack").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": [
    {
      "url": "https://example.com"
    }
  ]
}' |
apify call mendelamavc/web-evidence-pack --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Wa9907Mko6jBd6Ygj/builds/TukqhdwMwyHWRPGIV/openapi.json
