# Cookie Consent Checker: GDPR Cookie & Tracker Scanner (`latamdatatools/cookie-consent-scanner`) Actor

Scan websites for GDPR / ePrivacy cookie compliance in a real browser: cookies and trackers (Google Analytics, Meta Pixel, TikTok, Hotjar...) loaded before consent, consent banner and CMP (OneTrust, Cookiebot, Didomi, Usercentrics...), Google Consent Mode v2 and a risk note per site.

- **URL**: https://apify.com/latamdatatools/cookie-consent-scanner.md
- **Developed by:** [Julian Genuario](https://apify.com/latamdatatools) (community)
- **Categories:** Developer tools, SEO tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 website scanneds

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

## Cookie Consent Checker: GDPR Cookie & Tracker Scanner

**Check websites for GDPR and ePrivacy cookie compliance in bulk.** Each site is opened in a fresh real browser (headless Chromium) and left alone, **without clicking the cookie banner**, to see what happens **before the visitor gives consent**:

- Which **cookies** are already set, by category (advertising, analytics, heatmap, social, necessary, consent) and vendor
- Which **trackers** already load: Google Analytics, Google Ads / DoubleClick, Meta (Facebook) Pixel, TikTok Pixel, LinkedIn Insight, Microsoft Ads, Hotjar, Clarity, Pinterest, Snap, Reddit, Criteo, HubSpot and more
- Which **consent management platform (CMP)** is used: OneTrust, Cookiebot, Didomi, Usercentrics, Quantcast Choice, TrustArc, CookieYes, Complianz, iubenda, Osano, Termly, Axeptio, Sourcepoint, consentmanager, Google Funding Choices and others, plus the IAB **TCF** and **GPP** APIs
- Whether a **consent banner** shows and whether it has a visible **reject** option on the first layer
- **Google Consent Mode** (v1 / v2): default state, and whether Google tags report storage as denied (`gcs=G100`) or granted (`G111`) before any interaction
- A **risk note** (high / medium / low, 0-100) with the reasons in plain English

### Who is it for?

- **Privacy, legal and compliance teams** auditing their brands' websites or a portfolio of sites
- **Agencies and consultants** selling cookie-banner or Consent Mode v2 implementations: find prospects whose sites fire pixels before consent
- **Marketing and analytics teams** checking that tags respect consent after a release
- **DPOs and auditors** who need a quick, repeatable evidence list per site

### How to use the Cookie Consent Checker

1. Click **Try for free** (a free Apify account is enough).
2. Paste domains or URLs (or load a CSV / Google Sheet / dataset).
3. Optionally choose mobile, the browser language and how long to wait after load.
4. Click **Start**. The *Overview* table shows the risk, CMP, banner, Consent Mode and counts; the *Issues and trackers* view lists every cookie and tracker found. Export to CSV, Excel or JSON.
5. Schedule it weekly to catch regressions after site releases.

### Output

One row per website:

```json
{
  "url": "https://www.example-shop.com/",
  "status": "ok",
  "risk": { "level": "high", "score": 95, "reasons": [
    "9 advertising cookie(s) set before consent (Meta (Facebook), Google Ads, Reddit, Microsoft Ads)",
    "advertising trackers load before consent (Reddit Pixel, Meta Pixel (Facebook), Google Ads / DoubleClick)",
    "Google Consent Mode reports storage granted before any interaction" ] },
  "cmp": { "detected": true, "names": ["OneTrust"], "tcfApi": false, "gppApi": false },
  "banner": { "detected": true, "rejectButton": null, "text": null },
  "consentMode": { "detected": true, "version": "v2", "deniedByDefault": null, "grantedBeforeInteraction": true, "gcsValues": ["G111"] },
  "trackersBeforeConsent": [{ "vendor": "Meta Pixel (Facebook)", "category": "advertising", "hosts": ["connect.facebook.net", "www.facebook.com"], "requests": 4 }],
  "cookiesBeforeConsent": [{ "name": "_fbp", "domain": ".example-shop.com", "firstParty": true, "category": "advertising", "vendor": "Meta (Facebook)", "expiresInDays": 90, "secure": false, "httpOnly": false, "sameSite": "Lax" }],
  "counts": { "cookies": 36, "firstPartyCookies": 26, "thirdPartyCookies": 10, "advertisingCookies": 9, "analyticsCookies": 7, "necessaryCookies": 4, "unknownCookies": 16, "trackers": 6, "thirdPartyHosts": 33, "requests": 440 },
  "thirdPartyHosts": ["bat.bing.com", "connect.facebook.net", "..."]
}
```

Cookie **values are never stored**, only names, domains, lifetime and flags.

### How the risk is scored

| Finding before consent | Effect |
|---|---|
| Advertising cookies set | +40 |
| Advertising trackers loaded (unless only Google tags with Consent Mode denied by default) | +25 |
| Analytics / heatmap cookies set | +20 |
| Tracking but no banner and no CMP | +20 |
| Consent Mode reports storage granted before interaction | +10 |
| Analytics scripts loaded without cookies | +8 |
| Banner without a visible reject option on the first layer | +7 |

40+ = **high**, 15-39 = **medium**, below 15 = **low**. This is a technical indicator to prioritise reviews, **not legal advice**: what is allowed depends on the country, the legal basis and the purpose of each cookie.

### Pricing

Pay per event: **USD 5 per 1,000 websites scanned** (USD 0.005 per site). **Sites that fail to load, sites whose robots.txt disallows the page, invalid inputs and duplicates are free.** The run stops before going over your *max charge per run*.

### Limits and good to know

- **Location matters.** The scan runs from Apify's servers without proxies. Many sites show a consent banner (or block tags) only to visitors from the EU / UK, so results reflect what a visitor from the server's location sees.
- Nothing is clicked or scrolled, so the result is strictly the **pre-consent state**. Interactions that some CMPs treat as consent (scrolling) are avoided on purpose.
- Sites with strong bot protection (Cloudflare, Akamai, DataDome challenge pages) may block headless browsers: they come back as `failed` and are not charged. No CAPTCHA solving or evasion is attempted.
- Cookie and tracker lists cover the most common vendors; unrecognised cookies are reported as `unknown` with their domain so you can review them.
- robots.txt is respected for every page; one fresh browser profile per site; no logins.

### Why use the Cookie Consent Checker

- **Real browser, pre-consent state:** a fresh Chromium profile per site, nothing clicked or scrolled, so you see exactly what fires before the visitor decides.
- **Consent Mode v2 included:** the dataLayer `consent default` and the `gcs` / `gcd` values that Google tags actually send, not just whether gtag is present.
- **One row per site with a risk note:** high / medium / low with the reasons in plain English, ready to sort a portfolio or a prospect list.
- **Bulk input:** a list, a CSV or Google Sheet, or the dataset of another Actor (for example a lead list), keeping your own columns with `keepFields`.
- **Fair pricing:** failed sites, bot-protection challenge pages, robots-blocked pages, invalid inputs and duplicates are free.
- **Privacy-safe output:** cookie values are never stored.

### Cost examples

Prices at the current pay-per-event rate (USD 5 per 1,000 websites; see Pricing). You can set a maximum charge per run in the run options.

| Job | Approximate cost |
|---|---|
| The prefilled example: 2 sites (measured: 22 s) | about $0.01 |
| Monthly check of 50 brand sites | about $0.25 |
| 1,000 prospect websites for a Consent Mode v2 campaign | about $5.00 |

### Use it from your code, an integration or an AI agent

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("latamdatatools/cookie-consent-scanner").call(run_input={
  "urls": ["https://www.allbirds.com/", "example.com"]
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["url"], item["risk"]["level"], item["risk"]["reasons"])
```

**JavaScript / Node.js** (`npm install apify-client`):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('latamdatatools/cookie-consent-scanner').call({
  urls: ['https://www.allbirds.com/', 'example.com'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**No code:**

- **Schedules:** save your sites as a Task and run it weekly from Apify Console (Schedules) to catch regressions after releases.
- **Integrations:** send results to Google Sheets, Slack, email, Make, Zapier, n8n or Airbyte from the Integrations tab, or call your own webhook when a run finishes.
- **AI agents:** add it as a tool in Claude, Cursor, VS Code and other MCP clients through the Apify MCP server (`https://mcp.apify.com`), so the agent can scan a site and explain the findings.

### FAQ

**Does it accept or reject the banner?** No. It records the state before any choice, which is what regulators usually check first.

**Can I scan subpages?** Yes, pass full URLs (product page, checkout landing page). Each URL is one scan.

**Does it check Google Consent Mode v2?** Yes: the `consent default` command in the dataLayer (ad_storage, analytics_storage, ad_user_data, ad_personalization) and the `gcs` / `gcd` parameters that Google tags send.

**Can I see what an EU visitor sees?** The scan runs from Apify's servers (mostly in the US) without a proxy, so sites that geo-target their banner may look different. The browser language can be set with `locale`; the result always reflects the server's location.

**Is a "high" risk a legal violation?** Not necessarily. The score flags what usually needs a closer look (advertising cookies or pixels before consent); whether it is allowed depends on the law that applies and the purpose of each cookie. It is not legal advice.

Questions or a missing vendor? Open an issue in the Issues tab.

# Actor input Schema

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

Domains or page URLs, one per line (example.com, https://www.example.com/shop).

## `datasetId` (type: `string`):

Read websites from another run's dataset.

## `csvUrl` (type: `string`):

Public CSV, or a Google Sheet shared as "Anyone with the link".

## `urlField` (type: `string`):

Column (CSV) or field (dataset) with the website.

## `keepFields` (type: `array`):

Copied into a 'source' object on each row.

## `waitSecs` (type: `integer`):

Time to let tags and pixels fire after the page loads. Nothing is clicked or scrolled.

## `viewport` (type: `string`):

Desktop or mobile browser.

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

Accept-Language / navigator.language, e.g. en-GB, de-DE, fr-FR, es-ES.

## `pageTimeoutSecs` (type: `integer`):

Max time to load each page.

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

Browser tabs at the same time. Capped at one per GB of memory (1 GB = 1 tab).

## `includeThirdPartyHosts` (type: `boolean`):

Adds every third-party host contacted before consent (up to 200).

## Actor input object example

```json
{
  "urls": [
    "https://www.allbirds.com/",
    "example.com"
  ],
  "urlField": "url",
  "waitSecs": 6,
  "viewport": "desktop",
  "locale": "en-GB",
  "pageTimeoutSecs": 45,
  "maxConcurrency": 2,
  "includeThirdPartyHosts": true
}
```

# Actor output Schema

## `results` (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.allbirds.com/",
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("latamdatatools/cookie-consent-scanner").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.allbirds.com/",
        "example.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("latamdatatools/cookie-consent-scanner").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.allbirds.com/",
    "example.com"
  ]
}' |
apify call latamdatatools/cookie-consent-scanner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,latamdatatools/cookie-consent-scanner"
        }
    }
}
```

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/9cosxdlwawj0Vqlji/builds/w72dvI2fCINbc9IBP/openapi.json
