# SiteVerdict — Vendor Terms & Privacy Policy Change Monitor (`siteverdict/siteverdict-vendor-terms-monitor`) Actor

Watch vendors' terms, privacy policies, DPAs and pricing pages. Each run diffs against the last snapshot and flags material changes (liability caps, AI training on your data, data sharing, price, arbitration) with before/after excerpts, HTML diffs and Slack/webhook alerts.

- **URL**: https://apify.com/siteverdict/siteverdict-vendor-terms-monitor.md
- **Developed by:** [Aman Thakran](https://apify.com/siteverdict) (community)
- **Categories:** Business, Automation
- **Security:** Limited permissions
- **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

## SiteVerdict — Vendor Terms & Privacy Policy Change Monitor

**Know the day a vendor quietly changes its terms. Get the clauses that matter — liability caps, AI training on your data, data sharing, price, arbitration — highlighted before/after, with a Slack or webhook alert.**

> **Not legal advice.** This Actor detects and classifies text changes automatically. A change flagged (or not flagged) as "material" is a heuristic, not a legal opinion. Always read the full current document and take professional advice before relying on it.

### What you get

Schedule it daily or weekly with your list of vendor documents (Terms of Service, privacy notices, DPAs, acceptable-use policies, SLAs, pricing pages). The first run saves a baseline snapshot; every later run:

1. **Fetches each document politely** — honest user agent, robots.txt respected, 2-second gap per host, no bot-protection bypass.
2. **Extracts the document text** — navigation, headers, footers, scripts and cookie banners are stripped so site chrome does not create false alarms. Optional CSS selector per document.
3. **Diffs against the last snapshot** paragraph by paragraph, then sentence by sentence.
4. **Classifies every change** into 14 material-clause categories and scores severity (high / medium / low):
   - AI / ML training on customer data
   - limitation of liability
   - indemnity
   - data use, sharing or sale
   - price, fees, billing, auto-renewal (plus explicit price-token diff on pricing pages: `£12 per user / month → £15 per user / month`)
   - termination / suspension
   - governing law, jurisdiction, arbitration, class-action waivers
   - data retention / deletion
   - security / breach notification
   - sub-processors / international transfers
   - unilateral changes / notice periods
   - SLA / uptime / service credits
   - IP, licence and content ownership
   - confidentiality
5. **Ignores cosmetic noise** — whitespace, punctuation, curly quotes and "Last updated" date-only changes are reported as `cosmetic`, never alerted.
6. **Alerts** Slack and/or your webhook with one digest per run for changes at or above your chosen severity.
7. **Optional AI summary** on *your own* Anthropic or OpenAI key: a plain-English "what changed and why it matters" for documents with material changes.

### Who it is for

- **Procurement, vendor-risk and DPO teams** tracking dozens of SaaS suppliers' terms, DPAs and sub-processor clauses for UK GDPR / EU GDPR accountability.
- **Legal and commercial teams** who need to know when a supplier changes liability caps, renewal terms or dispute resolution.
- **Founders and finance** watching competitors' or suppliers' pricing pages.
- **AI agents** via the SiteVerdict MCP server (see `MCP-TOOL.md`).

### Example

Input:

```json
{
  "targets": [
    { "url": "https://www.example-saas.com/legal/terms", "vendor": "Example SaaS", "docType": "terms" },
    { "url": "https://www.example-saas.com/legal/dpa", "docType": "dpa" },
    { "url": "https://www.example-saas.com/pricing", "docType": "pricing" }
  ],
  "alertMinSeverity": "medium",
  "slackWebhookUrl": "https://hooks.slack.com/services/…"
}
```

Try it without any URLs: set **Sample mode** — it runs on bundled fictional documents (version 1 → version 2) and is free.

#### Before / after (from sample mode)

| Severity | Category | Before | After |
|---|---|---|---|
| high | Limitation of liability | …shall not exceed the fees paid by you in the «12 months» before the claim. | …shall not exceed the fees paid by you in the «3 months» before the claim. |
| high | AI / ML training on customer data | — | We may use your content and usage data to «train and improve our machine learning models» unless you opt out… |
| high | Governing law, jurisdiction, arbitration | — | Any dispute will be resolved by «binding arbitration» and you «waive any right to bring a class action». |
| medium | Unilateral changes / notice period | We will give you at least «30 days notice»… | «We may modify these terms at any time». «Continued use … constitutes acceptance»… |
| high | Price | £12 per user / month | £15 per user / month |
| none | cosmetic | Last updated: 10 January 2026 | Last updated: 2 October 2026 |

### Output

- **Dataset**, one row per document. Views:
  - **Overview** — status (`baseline`, `unchanged`, `changed`, `cosmetic`, `gone`, `blocked`, `robots-disallowed`, `error`, `skipped_budget` = not checked because the run's max total charge was reached), severity, categories, summary, AI summary, diff link.
  - **Material changes** — one row per material clause change with highlighted before/after text.
- **Key-value store**:
  - `REPORT` — HTML report of the run;
  - `diff-doc-…` — one HTML diff page per changed document (linked from `diffUrl`);
  - `CHANGES.csv` — spreadsheet export (formula-injection safe);
  - `SUMMARY` — agent-ready JSON.
- **Snapshot store** (named, default `siteverdict-vendor-terms`) — last snapshot + 20-entry history per document. Keep the same name across scheduled runs.

### Pricing (pay per event)

| Event | When | Price |
|---|---|---|
| `page-checked` | Each document successfully checked (baseline, unchanged, changed or cosmetic) | $0.004 |
| `material-change` | Each document with at least one material change in this run | $0.03 |
| `ai-summary` | Each successful AI summary (your provider bills the tokens separately) | $0.01 |

Never charged: blocked pages, robots-disallowed pages, errors, documents not checked because the max total charge was reached (`skipped_budget`), invalid input, sample mode. The Actor stops before exceeding your **Max total charge**: documents it could not afford still get a `skipped_budget` row (not fetched, snapshot unchanged), and the run status and SUMMARY say how many were not checked.

Examples: 50 vendor documents checked weekly ≈ **$0.20 per run** (~$0.87/month) plus $0.03 per document that changes materially. 200 documents daily ≈ $0.80/day.

### FAQ

**Does it work on JavaScript-only pages?** It reads the server-rendered HTML. Most legal pages are static; if a page needs JavaScript, the row says "No readable document text found" and nothing is charged.

**What about PDFs?** HTML and plain-text documents only in v1. PDF DPAs are reported as unsupported (not charged).

**Will a redesign of the vendor's site flood me with alerts?** Navigation, footers and cookie banners are stripped, and paragraphs are aligned by content, so layout changes do not count. If a document is completely restructured, the change list is capped and flagged.

**Can it log in to vendor portals?** No. Only public pages; login-protected and bot-protected pages are reported as `blocked` and never bypassed.

**Where does the AI summary run?** On your own Anthropic or OpenAI account, with your key (stored as a secret, never logged). Only the changed excerpts are sent. Off by default.

### Limits

- Up to 200 documents per run; 4 MB per page; first 400,000 characters of each document are stored.
- Clause classification uses curated keyword/phrase rules (no AI unless you enable it): expect some misses and some false positives on unusual wording. Severity is a triage aid.
- Pages that change on every load (A/B tests, personalised text) may show minor wording changes.

### Sources & compliance

- **Data source:** the public URLs you provide. We fetch them with the honest user agent `SiteVerdict/vendor-terms-monitor (+hello@webrevenuescan.com)`, respect robots.txt (RFC 9309; a robots.txt server error is treated as "disallow"), space requests per host and never bypass CAPTCHAs, logins, paywalls or bot protection. You are responsible for having the right to monitor the pages you list and for complying with each site's terms of use. Checked 11 Oct 2026.
- **Security:** every URL and every redirect hop is checked against private, loopback, link-local and cloud-metadata addresses (SSRF guard, re-checked at connect time). Webhook URLs must be HTTPS. Secret inputs are masked from logs, dataset and files.
- **Personal data:** the Actor stores the text of public legal documents, which normally contains no personal data beyond business contact details published by the vendor. It does not collect personal data about individuals. UK GDPR / PECR: if you add pages that do contain personal data, you are the controller for that processing.
- **Copyright:** snapshots of third-party documents are kept in your own Apify storage for comparison; do not republish them.
- **Not legal advice.** See the disclaimer at the top.

# Changelog

This Actor's version history is a separate document: https://apify.com/siteverdict/siteverdict-vendor-terms-monitor/changelog.md

# Actor input Schema

## `targets` (type: `array`):

One object per document: <code>{"url": "https://…", "vendor": "Acme", "docType": "terms|privacy|dpa|pricing|aup|sla|other", "label": "Master subscription agreement", "selector": "main .legal"}</code>. Only <code>url</code> is required; the vendor defaults to the domain and the document type is guessed from the URL. Optional <code>selector</code> is a CSS selector for the document body. Up to 200 documents per run.

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

Alternative to the JSON above: one URL per line. Combined with the documents above.

## `sampleMode` (type: `boolean`):

Runs on bundled fictional fixtures (terms, privacy notice and pricing page, version 1 → version 2) so you can see the diff, report and alerts without fetching anything. Nothing is charged.

## `stateStoreName` (type: `string`):

Named key-value store holding the last snapshot of every document. Keep the same name across scheduled runs; use different names for separate watch-lists. Letters, digits and dashes.

## `alertMinSeverity` (type: `string`):

Minimum severity that triggers a Slack / webhook alert. Cosmetic changes (whitespace, punctuation, only the 'last updated' date) never alert.

## `slackWebhookUrl` (type: `string`):

Optional. HTTPS Slack incoming-webhook URL; one digest message per run listing documents that changed at or above the alert severity. Stored as a secret and never logged.

## `webhookUrl` (type: `string`):

Optional. HTTPS endpoint that receives one JSON digest per run (vendor, URL, severity, categories, summary, diff link). Stored as a secret and never logged.

## `aiProvider` (type: `string`):

Adds a plain-English 'what changed and why it matters' summary to documents with material changes, using YOUR OWN Anthropic or OpenAI API key (billed by your provider). Off by default. Only the changed excerpts are sent to the provider.

## `aiApiKey` (type: `string`):

Your Anthropic or OpenAI API key. Stored as a secret, never logged or written to the output.

## `aiModel` (type: `string`):

Model ID. Defaults: claude-sonnet-5-5 (Anthropic) or gpt-4.1-mini (OpenAI).

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

Pages disallowed for our crawler are skipped (reported, not charged). Keep this on unless the page belongs to you.

## `outputUnchanged` (type: `boolean`):

When off, the dataset only contains documents that changed, new baselines and problems.

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

Documents fetched in parallel. Requests to the same host are always spaced at least 2 s apart.

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

Maximum time for one HTTP request (each redirect hop and retry has its own timeout).

## Actor input object example

```json
{
  "targets": [
    {
      "url": "https://www.gov.uk/help/terms-conditions",
      "vendor": "GOV.UK",
      "docType": "terms",
      "label": "GOV.UK terms and conditions"
    },
    {
      "url": "https://www.gov.uk/help/privacy-notice",
      "vendor": "GOV.UK",
      "docType": "privacy",
      "label": "GOV.UK privacy notice"
    }
  ],
  "sampleMode": false,
  "stateStoreName": "siteverdict-vendor-terms",
  "alertMinSeverity": "medium",
  "aiProvider": "none",
  "respectRobotsTxt": true,
  "outputUnchanged": true,
  "maxConcurrency": 3,
  "timeoutSecs": 30
}
```

# Actor output Schema

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

No description

## `files` (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 = {
    "targets": [
        {
            "url": "https://www.gov.uk/help/terms-conditions",
            "vendor": "GOV.UK",
            "docType": "terms",
            "label": "GOV.UK terms and conditions"
        },
        {
            "url": "https://www.gov.uk/help/privacy-notice",
            "vendor": "GOV.UK",
            "docType": "privacy",
            "label": "GOV.UK privacy notice"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("siteverdict/siteverdict-vendor-terms-monitor").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 = { "targets": [
        {
            "url": "https://www.gov.uk/help/terms-conditions",
            "vendor": "GOV.UK",
            "docType": "terms",
            "label": "GOV.UK terms and conditions",
        },
        {
            "url": "https://www.gov.uk/help/privacy-notice",
            "vendor": "GOV.UK",
            "docType": "privacy",
            "label": "GOV.UK privacy notice",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("siteverdict/siteverdict-vendor-terms-monitor").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 '{
  "targets": [
    {
      "url": "https://www.gov.uk/help/terms-conditions",
      "vendor": "GOV.UK",
      "docType": "terms",
      "label": "GOV.UK terms and conditions"
    },
    {
      "url": "https://www.gov.uk/help/privacy-notice",
      "vendor": "GOV.UK",
      "docType": "privacy",
      "label": "GOV.UK privacy notice"
    }
  ]
}' |
apify call siteverdict/siteverdict-vendor-terms-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,siteverdict/siteverdict-vendor-terms-monitor"
        }
    }
}
```

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/JnncjhfaarHvPZBbA/builds/5NXRnx11AMIldzQld/openapi.json
