# Website Legal Compliance Check: Imprint, Privacy, Cookies (`accountable_eel/website-legal-compliance-check`) Actor

Website legal compliance checker for a list of domains. One row per site: imprint, privacy policy, cookie policy, terms, returns policy and accessibility statement found or missing, each with its URL, plus the cookie-consent platform the page loads. Sites that cannot be read are never charged.

- **URL**: https://apify.com/accountable_eel/website-legal-compliance-check.md
- **Developed by:** [Adriana Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 website checkeds

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 Legal Compliance Check: Imprint, Privacy, Cookies

Paste a list of domains and get one row per website saying which legal pages it publishes:
**imprint, privacy policy, cookie policy, terms, returns policy and accessibility statement**, each
found or missing and each with its address, plus the **cookie-consent platform** the page loads
(OneTrust, Cookiebot, Usercentrics, Sourcepoint and 20 more). A website legal compliance checker
for lists, not one site at a time. No API key, no account, no browser.

### Who it's for

- **Agencies and freelancers** auditing a client list or a prospect list: which sites have no
  imprint, no privacy policy or no consent platform is a ready reason to get in touch.
- **Compliance, legal and privacy teams** keeping an inventory of every domain the company owns and
  what each one publishes.
- **Ecommerce operators and marketplaces** checking that stores link their terms and returns
  policy before onboarding them.
- **Accessibility consultants** finding the sites in a sector that publish no accessibility
  statement.
- **Researchers** measuring how many sites in a sample publish each document or load each consent
  platform.

### What you get

One flat row per website:

| What | Columns |
| --- | --- |
| The summary | `domain`, `siteUrl`, `documentsFound`, `documentsMissing` |
| Each document, found or not, and where | `hasImprint` + `imprintUrl`, `hasPrivacyPolicy` + `privacyPolicyUrl`, `hasCookiePolicy` + `cookiePolicyUrl`, `hasTerms` + `termsUrl`, `hasReturnsPolicy` + `returnsPolicyUrl`, `hasAccessibilityStatement` + `accessibilityStatementUrl` |
| Cookie consent | `hasConsentPlatform`, `consentPlatform`, `hasConsentSettingsLink`, `hasIabTcfApi`, `hasGoogleConsentMode` |
| The company, when the page declares it | `organizationName`, `legalName`, `vatId` |
| Evidence | `findings`, `legalPageUrl`, `language`, `pagesRead`, `notes`, `sourceUrl`, `scrapedAt` |

Three example rows, shortened. Read on 2026-10-11:

| domain | documentsFound | documentsMissing | hasImprint | hasPrivacyPolicy | hasTerms | hasReturnsPolicy | hasAccessibilityStatement | consentPlatform |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| allbirds.com | 3 | imprint, cookie policy, accessibility statement | false | true | true | true | false | OneTrust |
| python.org | 1 | imprint, cookie policy, terms, returns policy, accessibility statement | false | true | false | false | false | |
| heise.de | 4 | cookie policy, terms | true | true | false | true | true | Sourcepoint |

`findings` holds the proof for every document found: its address, the text of the link, and how it
was found.

| `foundBy` | Meaning |
| --- | --- |
| `link` | The page links to the document. The address is the link's. |
| `section-on-page` | The document is a section of the page itself, under a heading with its name. The address is the page it sits on. |
| `structured-data` | The page declares a returns policy in its schema.org markup. |

### How to use Website Legal Compliance Check

1. **In the Apify Console.** Open the actor page and click **Start** — the `domains` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~website-legal-compliance-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"domains":["allbirds.com","python.org"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

### Input

```json
{
  "domains": [
    "allbirds.com",
    "python.org"
  ]
}
```

One website per line: a domain such as allbirds.com, or a URL. Each site's homepage is read once (robots.txt first) and you get one row saying which legal pages it publishes: imprint, privacy policy, cookie policy, terms, returns policy and accessibility statement, plus the cookie-consent platform it loads. Accepted formats: allbirds.com, www.python.org, https://www.heise.de/.

### Output

| query | found | status | domain | siteUrl | documentsFound | documentsMissing | hasImprint | imprintUrl | hasPrivacyPolicy | privacyPolicyUrl | hasCookiePolicy | cookiePolicyUrl | hasTerms | termsUrl | hasReturnsPolicy | returnsPolicyUrl | hasAccessibilityStatement | accessibilityStatementUrl | hasConsentPlatform | consentPlatform | consentPlatforms | hasConsentSettingsLink | hasIabTcfApi | hasGoogleConsentMode | legalPageUrl | organizationName | legalName | vatId | language | findings | pagesRead | notes | sourceUrl | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| allbirds.com | true | OK | <domain> | <homepage read> | <documents found> | <documents missing> | <imprint> | <imprint url> | <privacy policy> | <privacy policy url> | <cookie policy> | <cookie policy url> | <terms> | <terms url> | <returns policy> | <returns policy url> | <accessibility statement> | <accessibility statement url> | <consent platform loaded> | <consent platform> | \<consent platforms (list)> | <consent settings link> | <iab tcf api> | <google consent mode default> | <legal page> | <organisation name> | <legal name> | <vat id> | <page language> | <findings> | <pages read> | <notes> | <source url> | 1970-01-01T00:00:00.000Z |

A line that is not a website, a site that refuses the request, a site that does not answer and a
site whose robots.txt disallows its homepage come back as one row with `"found": false` and a
`message` that says why. They are never charged.

### Example runs

Three inputs that need no credentials of any kind and return rows as they stand. Paste one into the
Input tab and press Start.

**1. Which legal pages do these sites publish, and which consent platform do they load?**

```json
{
  "domains": ["allbirds.com", "python.org"]
}
```

**2. German sites: Impressum, Datenschutz, Widerruf and Barrierefreiheit**

```json
{
  "domains": ["heise.de", "www.python.org", "https://www.allbirds.com/"]
}
```

**3. A longer list: only the sites that could be read, at most 100 rows**

```json
{
  "domains": ["allbirds.com", "python.org", "heise.de"],
  "onlyFound": true,
  "maxResults": 100
}
```

### Pricing

- **Website checked**: $12 per 1,000 websites

Plus a $0.001 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

You pay once per website whose homepage was read, and nothing else:

- A site with none of the six documents is a result ("all missing") and is charged.
- A blocked site, an unreachable site, a robots.txt refusal, a homepage with nothing in its HTML to
  read, and a line that is not a website are free.
- A domain listed twice is checked and charged once.
- A row hidden by "Only keep rows containing" is not returned and not charged.
- Use "Stop after this many results" to put a ceiling on a long list.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~website-legal-compliance-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"domains":["allbirds.com","python.org"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~website-legal-compliance-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"domains":["allbirds.com","python.org"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~website-legal-compliance-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"domains":["{{website}}"]}`, mapping the row's website into the `domains` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "website legal compliance checker" — the agent will find and run this actor.

### Where the data comes from

- **The documents** come from the links and headings of the site's own homepage, read once as
  plain HTML. When the homepage links a page that collects the site's legal texts and a document is
  still missing, that one page is read as well. At most three requests per site, robots.txt
  included.
- **The consent platform** is recognised by the script or the setting each platform puts in the
  page source.
- **The company name, legal name and VAT ID** come from the page's schema.org Organization markup
  and are empty when the page has none.

Link texts and addresses are recognised in English, German, French, Spanish, Italian, Portuguese,
Dutch, Swedish, Danish, Norwegian, Finnish and Polish.

### Limits, stated plainly

- **This is a presence check, not legal advice.** It says whether a document is published and
  where. It does not read the document, judge whether it is complete or say that a site complies
  with any law. Which documents a site needs depends on its country and what it does: a returns
  policy matters for a store, an imprint is a legal duty in Germany and Austria and unknown in the
  United States.
- **"Consent platform loaded" is not "banner shown".** The row says that the platform's script is
  in the page source. Whether a visitor sees a banner, and whether it blocks cookies before
  consent, can only be seen in a browser.
- **A consent banner the site built itself is not recognised**, and neither is a platform that is
  only loaded later by a tag manager. `hasConsentPlatform: false` means "none of the 24 known
  platforms is in the page source".
- **No JavaScript is run.** A site that builds its footer in the browser is returned as
  `NO_STATIC_CONTENT` and is free, because nothing can be said about it.
- **The homepage is the evidence.** A document linked only from a checkout page or a sub-page is
  reported as not found.
- **The links are not opened.** A link to a privacy policy that leads to an error page still counts
  as found. Run the addresses through Link Rot Checker to test them.
- **Some sites refuse automated requests.** Those rows say `BLOCKED` and are free.

### Data & privacy

The actor reads the public homepage of each site you list, after checking its robots.txt. It
returns the addresses of legal pages and, when the page declares one, the organisation behind the
site. It never reads a schema.org Person entry, returns no email addresses or phone numbers, and
stores nothing between runs.

### FAQ

**A site has a privacy policy, but the row says it is missing. Why?**
The homepage does not link it in its HTML. The link is either added by a script after the page
loads, sits on another page, or uses a wording the check does not know. `findings` shows what was
found and `notes` says what was read.

**What is the difference between `hasCookiePolicy` and `hasConsentPlatform`?**
A cookie policy is a page that explains the cookies. A consent platform is the tool that asks the
visitor for permission. A site can have either without the other.

**What does `section-on-page` mean?**
Some stores put the whole privacy policy or the terms inside the homepage, opened by a button,
instead of on a page of their own. The document exists, so it is reported as found, and its address
is the homepage.

**Can I check thousands of sites?**
Yes. Sites are checked three at a time by default and up to ten at a time. A site takes a second or
two.

**Do I need a proxy?**
No. There is nothing to set up.

### Related actors

- **AI Crawler Policy Lookup** for what a site's robots.txt says to AI crawlers.
- **Security Headers Lookup** for a site's HTTP security headers.
- **Tech Stack Lookup** for the platforms and tools a site runs on.
- **Website Contact Extractor** for a site's contact details.
- **Link Rot Checker** to test that the legal pages found here still open.

# Actor input Schema

## `domains` (type: `array`):

One website per line: a domain such as allbirds.com, or a URL. Each site's homepage is read once (robots.txt first) and you get one row saying which legal pages it publishes: imprint, privacy policy, cookie policy, terms, returns policy and accessibility statement, plus the cookie-consent platform it loads. Accepted formats: allbirds.com, www.python.org, https://www.heise.de/. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

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

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## Actor input object example

```json
{
  "domains": [
    "allbirds.com",
    "python.org"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "columns": [
    "domain",
    "siteUrl",
    "documentsFound",
    "documentsMissing",
    "hasImprint",
    "imprintUrl",
    "hasPrivacyPolicy",
    "privacyPolicyUrl",
    "hasCookiePolicy",
    "cookiePolicyUrl",
    "hasTerms",
    "termsUrl",
    "hasReturnsPolicy",
    "returnsPolicyUrl",
    "hasAccessibilityStatement",
    "accessibilityStatementUrl",
    "hasConsentPlatform",
    "consentPlatform",
    "consentPlatforms",
    "hasConsentSettingsLink",
    "hasIabTcfApi",
    "hasGoogleConsentMode",
    "legalPageUrl",
    "organizationName",
    "legalName",
    "vatId",
    "language",
    "findings",
    "pagesRead",
    "notes",
    "sourceUrl"
  ],
  "maxConcurrency": 3
}
```

# Actor output Schema

## `results` (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 = {
    "domains": [
        "allbirds.com",
        "python.org"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/website-legal-compliance-check").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 = {
    "domains": [
        "allbirds.com",
        "python.org",
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/website-legal-compliance-check").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 '{
  "domains": [
    "allbirds.com",
    "python.org"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/website-legal-compliance-check --silent --output-dataset

```

## MCP server setup

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

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/YHzdQycZ8r8gvmGV2/builds/fTHaraEhZO8eQEHEX/openapi.json
