# Cookie & Tracker Audit: GDPR Consent Checker (`offerastudio/cookie-tracker-audit`) Actor

Bulk-check websites for cookies and tracking tags that load before consent (GDPR / ePrivacy). Get every cookie with category and vendor, 70+ tags such as Meta Pixel, Google Analytics and TikTok with evidence, the consent banner (CMP) found, and plain-English risk flags. Not legal advice.

- **URL**: https://apify.com/offerastudio/cookie-tracker-audit.md
- **Developed by:** [Offera Studio](https://apify.com/offerastudio) (community)
- **Categories:** Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 page auditeds

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

### What does Cookie & Tracker Audit do?

**Cookie & Tracker Audit** checks any list of web pages, up to 1,000 URLs per run, for what happens **before a visitor clicks anything on the cookie banner**. For every page you get:

- 🍪 **every cookie set before consent**: name, domain, first- or third-party, expiry, and a **category guessed from 280+ known cookie names** (analytics, ads, functional, unknown), with the **rule that matched** (for example `_ga_*` → Google Analytics)
- 🏷️ **third-party tags present before consent**: Google Analytics / GA4, Google Tag Manager, Meta Pixel, TikTok Pixel, LinkedIn Insight Tag, Hotjar, Microsoft Clarity, Google Ads, Pinterest, Snap, Reddit, X, Criteo, YouTube embeds and 50+ more, each with the **URL or code snippet that matched**
- ✋ whether each tag **loads immediately or waits for consent** (scripts held back with `type="text/plain"`, `data-cookieconsent`, OneTrust category classes or `data-src` are recognised)
- 🛡️ the **consent management platform (CMP)** found, if any: OneTrust, Cookiebot, Didomi, Usercentrics, iubenda, CookieYes, Complianz, Borlabs, Quantcast/InMobi Choice, TrustArc, Sourcepoint and 20+ others, plus **Google Consent Mode** defaults
- 🚩 a plain-English list of **risk flags** and a **risk level** (high, medium, low, none), for example *"Meta Pixel loads before consent and no consent banner (CMP) was detected."*

Paste your URLs, click **Start**, and download the results as JSON, CSV, Excel or HTML, or get them through the Apify API. You can schedule audits, call them from your own code, and send the results to Google Sheets, Slack, Zapier, Make and other tools.

> **Not legal advice.** The Actor reports what it can observe and says "likely", never "illegal". Whether a cookie or tag needs consent depends on its purpose, your legal basis and the country. Ask a qualified advisor for legal questions.

### Who is this cookie checker for?

- **Web agencies and freelancers**: screen a client's or prospect's sites in minutes and show exactly which tags fire before consent, with the evidence.
- **Privacy and compliance teams**: monitor all brand sites and landing pages on a schedule and catch a new pixel that a marketing team added without a consent block.
- **Consent management (CMP) consultants and resellers**: find sites with trackers but no consent banner.
- **Marketing and analytics teams**: check that Google Analytics, Meta Pixel and friends really wait for the banner after a site or tag manager change.
- **Due diligence and audits**: get a quick, repeatable inventory of cookies and third-party tags across a portfolio.

### What does it check?

| Area | What you get |
| --- | --- |
| Cookies | Every cookie present before consent. Static mode: cookies in `Set-Cookie` headers of the page and every redirect before it. Browser mode: also cookies set by JavaScript and third-party cookies. Name, domain, first/third party, category, vendor, matched rule, expiry in days, Secure, HttpOnly, SameSite. **Cookie values are never stored.** |
| Cookie categories | `analytics`, `ads` (advertising, marketing and social media), `functional` (sessions, security, load balancing, carts, language, consent storage) or `unknown`. Guessed from 280+ known names, and for third-party cookies from 60 known advertising and analytics domains. |
| Tags | Google Analytics, Google Tag Manager, Google Ads, DoubleClick, AdSense, Meta Pixel, TikTok, LinkedIn Insight, Microsoft Clarity and UET, Hotjar, X, Pinterest, Snap, Reddit, Criteo, Taboola, Outbrain, Amazon Ads, HubSpot, Klaviyo, Mixpanel, Segment, Amplitude, Heap, FullStory, Yandex Metrica, Adobe, Matomo and more. Cookieless analytics (Plausible, Fathom, Simple Analytics, Cloudflare Web Analytics, Umami) are listed but never flagged. |
| Consent block | Whether each tag loads right away or is held back until consent, and how (`blockedBy`). |
| Consent banner | 30+ CMPs by script, configuration or element id, IAB TCF, and consent banner scripts whose file name says so. |
| Google Consent Mode | Whether `gtag('consent', 'default', …)` is set and its `ad_storage`, `analytics_storage`, `ad_user_data`, `ad_personalization` defaults. |
| Risk | Plain-English `riskFlags`, a `riskLevel` and a one-sentence `summary`. |

### Static check or browser check?

| | Static (default) | Browser (`renderJavaScript: true`) |
| --- | --- | --- |
| Speed | Fast, 1 GB memory | Slower, 4 GB memory |
| Cookies | Only cookies the **server** sets in its HTTP response (and redirects) | Also cookies set by **JavaScript** and **third-party cookies** |
| Tags | Tags written in the HTML | Also every tag a **tag manager** fires, seen as real network requests |
| Price | $0.004 per page | **same price**, $0.004 per page |

Static mode is a quick first screen. **Most analytics and advertising cookies are set by JavaScript, so they only show up with "Render JavaScript" on.** In static mode, the row says so in `notes`, and a tag manager without a consent banner is flagged so you know to look deeper. In browser mode, each page opens in a fresh headless Chromium with no cookies, the Actor waits until the network is idle plus two seconds, and never clicks anything.

### How to run a cookie consent audit

1. Click **Try for free** and sign in to Apify (the free plan is enough to start).
2. Paste one or more URLs into **Website URLs**, or upload a text file with one URL per line.
3. Optional: set **Pages per website** to audit up to 20 pages per site (the start page plus pages linked from it).
4. Optional: turn on **Render JavaScript** to see cookies set by scripts and every tag fired before consent.
5. Click **Start**. Open the **Overview**, **Risk flags**, **Cookies** or **Tracking tags** tab when the run finishes.

### Input example

```json
{
    "startUrls": [{ "url": "https://www.example.com" }, { "url": "shop.example.org" }],
    "maxPagesPerSite": 3,
    "renderJavaScript": true
}
```

### Output example

One item per page (shortened, made-up data):

```json
{
    "url": "https://www.example.com/",
    "riskLevel": "high",
    "summary": "Likely issue: Meta Pixel, Google Analytics load before consent; 3 advertising/analytics cookie(s) set before consent (no consent banner detected).",
    "riskFlags": [
        "Meta Pixel loads before consent and no consent banner (CMP) was detected.",
        "Google Analytics loads before consent and no consent banner (CMP) was detected.",
        "Advertising cookies are set before consent: _fbp (Meta Pixel), fr (Meta (Facebook), facebook.com).",
        "Analytics cookies are set before consent: _ga (Google Analytics)."
    ],
    "cmpDetected": false,
    "cmpNames": [],
    "googleConsentMode": { "detected": false, "adStorage": null, "analyticsStorage": null, "adUserData": null, "adPersonalization": null },
    "cookiesCount": 4,
    "adsCookiesCount": 2,
    "analyticsCookiesCount": 1,
    "thirdPartyCookiesCount": 1,
    "cookies": [
        { "name": "_fbp", "domain": "example.com", "party": "first-party", "category": "ads", "vendor": "Meta Pixel", "matchedRule": "_fbp", "setBy": "browser", "expiresInDays": 90, "secure": false, "httpOnly": false, "sameSite": "Lax" },
        { "name": "fr", "domain": "facebook.com", "party": "third-party", "category": "ads", "vendor": "Meta (Facebook)", "matchedRule": "fr on facebook.com", "setBy": "browser", "expiresInDays": 90, "secure": true, "httpOnly": true, "sameSite": "None" },
        { "name": "_ga", "domain": "example.com", "party": "first-party", "category": "analytics", "vendor": "Google Analytics", "matchedRule": "_ga", "setBy": "browser", "expiresInDays": 400, "secure": false, "httpOnly": false, "sameSite": null },
        { "name": "PHPSESSID", "domain": "www.example.com", "party": "first-party", "category": "functional", "vendor": "Session (server framework)", "matchedRule": "PHPSESSID", "setBy": "http-header", "expiresInDays": null, "secure": true, "httpOnly": true, "sameSite": null }
    ],
    "trackersBeforeConsent": ["Meta Pixel", "Google Analytics"],
    "trackers": [
        { "vendor": "Meta Pixel", "category": "ads", "loadsBeforeConsent": true, "blockedBy": null, "matchedBy": "network-request", "evidence": "https://connect.facebook.net/en_US/fbevents.js", "cookieless": false, "note": null },
        { "vendor": "Google Analytics", "category": "analytics", "loadsBeforeConsent": true, "blockedBy": null, "matchedBy": "script-src", "evidence": "https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX", "cookieless": false, "note": null },
        { "vendor": "YouTube (embedded video)", "category": "ads", "loadsBeforeConsent": false, "blockedBy": "data-src instead of src (data-cookieconsent=\"marketing\")", "matchedBy": "iframe", "evidence": "https://www.youtube.com/embed/abc", "cookieless": false, "note": "Standard YouTube embeds can set YouTube/Google cookies. youtube-nocookie.com avoids this until the video plays." }
    ],
    "notes": ["Checked in a headless browser without clicking anything: cookies and requests are those present before any consent choice.", "…"],
    "disclaimer": "Automated, heuristic check of what happens before a visitor makes a consent choice. Not legal advice.",
    "error": null
}
```

Pages that can't be audited get a row with an `error` code (`invalid-url`, `blocked-by-robots-txt`, `http-404`, `http-403`, `not-html`, `request-failed`, …) and cost nothing.

### How is the risk level decided?

| Risk level | When |
| --- | --- |
| **high** | An advertising or analytics tag loads before consent **and no consent banner was found**, or advertising/analytics **cookies are already set** before consent. |
| **medium** | A tag loads without a consent block **although a consent banner was found**, or a tag manager loads with no banner (static mode can't see what it fires). |
| **low** | Only minor points: Google tags that load but default to "denied" in Google Consent Mode, or cookies whose purpose can't be guessed from the name. |
| **none** | No advertising or analytics tags or cookies found before consent. |

The wording is deliberately careful ("likely", "possible issue"). It is a way to find and prioritise pages to look at, **not a legal verdict**.

### How much does a cookie audit cost?

This Actor uses **pay per event**. You pay only for pages that were actually audited:

| Event | Price |
| --- | --- |
| Page audited | **$0.004** per page |
| Page that failed, was blocked by robots.txt or returned an error | **free** |

- 100 pages cost **$0.40**; 1,000 pages cost **$4**.
- The price is the same with or without browser rendering.
- Apify also charges a tiny standard start fee per run ($0.00005 per GB of memory, so $0.00005 for a normal run and $0.0002 with browser rendering).
- Apify's free plan includes $5 of monthly usage, enough for about 1,000 audited pages a month.
- Set **Maximum cost per run** in the run options and the Actor stops when it is reached.

### Limitations

- **Location matters.** The Actor runs on Apify's servers in the United States and uses no proxy. Many consent banners only hold tags back for visitors from the EU or UK, so a site can look worse here than it does for European visitors. When a consent banner is found and tracking still loads, the row says so in `notes`. With **Render JavaScript** on and OneTrust, `cmpVisitorCountry` shows the country the banner assigned (usually `US`), and the risk level is capped at medium.
- **Static mode only sees server cookies and tags written in the HTML.** Cookies set by JavaScript, third-party cookies and tags fired by a tag manager need **Render JavaScript**.
- **Nothing is clicked.** The check covers the state before any consent choice. It doesn't test whether "Reject all" works, or what happens after scrolling or a long delay.
- **Categories are guessed from names and domains.** Unknown cookies are listed as `unknown` so you can check them yourself; a first-party cookie with a generic name can't be categorised reliably.
- **CMP detection is by known signatures.** A self-built banner without a telling script name may not be recognised.
- The Actor **respects robots.txt** and waits at least one second between requests to the same site. Sites that block automated visitors return an error row (free). Login-protected pages are not supported.
- **This is not legal advice** and not a compliance certificate.

### FAQ

#### Does this tell me if my site is GDPR compliant?

No. It shows what loads and which cookies exist before a visitor makes a choice, and flags what is **likely** to need consent under the GDPR and the ePrivacy rules (cookie laws). Some cookies are strictly necessary and need no consent; some tags may be fine under your legal basis or configuration. Use the results to find what to look at, and ask a qualified advisor for legal decisions.

#### Why is a cookie marked "unknown"?

Its name isn't in the list of known cookies, and its domain isn't a known advertising or analytics domain. Site-specific cookies are often like that. The row lists them in a separate flag so you can check what they do.

#### Why do I see trackers although the site has a consent banner?

Either the tags are not connected to the banner, or the banner only blocks tags for visitors from certain countries. The Actor runs from the United States, so a banner that only applies to EU visitors will look inactive here. See `notes` and, in browser mode with OneTrust, `cmpVisitorCountry`.

#### Are cookie values stored?

No. Cookie values often contain unique visitor IDs, so the Actor only keeps the name, domain and attributes. It collects no personal data.

#### Can I run it on a schedule or from my code?

Yes. Create a task with your URLs and add a schedule in Apify Console, or call the Actor through the Apify API, the JavaScript or Python client, or integrations such as Make, Zapier and n8n.

#### Why did a page return `blocked-by-robots-txt` or `http-403`?

The site's robots.txt disallows crawlers for that page, or the site blocks automated visitors. These rows are free.

### More tools from the same developer

All pay-per-result, no proxy or login needed, built and maintained by the same developer:

**Website audits**

- [Website Accessibility Checker: WCAG 2.2 & EAA](https://apify.com/offerastudio/website-accessibility-audit): accessibility issues with fixes, SEO basics and security headers.
- [AI Crawler Access Checker: robots.txt & llms.txt](https://apify.com/offerastudio/ai-crawler-access-audit): which AI crawlers a site allows, plus llms.txt.
- [Website Change Monitor: Diffs, Prices & Alerts](https://apify.com/offerastudio/website-change-monitor): get a row only when a page changes, with a clean diff.

**Company data and compliance**

- [Company Contact Finder: Emails, Phones & Socials](https://apify.com/offerastudio/company-contact-finder): contact details published on company websites.
- [UK New Companies Feed: Companies House Daily](https://apify.com/offerastudio/uk-new-companies-feed): newly incorporated UK companies with sector filters.
- [EU VAT Number Validator: Bulk VIES Checker](https://apify.com/offerastudio/eu-vat-number-validator): bulk VAT checks with name, address and consultation number.
- [LEI Corporate Tree: GLEIF Parents & Subsidiaries](https://apify.com/offerastudio/gleif-lei-corporate-tree): LEI lookup with parents, subsidiaries and a KYC summary.

**Market signals**

- [US WARN Layoff Notices: 12 States Daily Feed](https://apify.com/offerastudio/us-warn-layoff-notices): layoff and plant closure notices from official state sources.
- [US Product Recalls Monitor: FDA & CPSC Feed](https://apify.com/offerastudio/us-product-recalls-monitor): FDA and CPSC recalls in one feed, with severity.

### Feedback

Found a cookie or tag that should be recognised, or a false alarm? Open an issue on the **Issues** tab with the page URL. New vendors are added to the maintained lists quickly.

# Changelog

This Actor's version history is a separate document: https://apify.com/offerastudio/cookie-tracker-audit/changelog.md

# Actor input Schema

## `startUrls` (type: `array`):

Pages to audit, up to 1,000 per run. Bare domains such as example.com work too. Each page becomes one row in the results. You can also upload a text file with one URL per line.

## `maxPagesPerSite` (type: `integer`):

1 = audit only the URLs you entered. Set up to 20 to also follow links on the same website (same hostname) and audit those pages too, for example a landing page, the shop and the blog. Every audited page is billed as one page.

## `renderJavaScript` (type: `boolean`):

Off (default): a fast static check of the cookies the server sets in its HTTP response and the tags written in the HTML. On: each page is opened in a real headless browser (Playwright + Chromium) without clicking anything, so you also see cookies set by JavaScript, third-party cookies and every tag a tag manager fires before consent. Slower and uses more memory (4 GB by default), same price per page.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://apify.com"
    },
    {
      "url": "https://crawlee.dev"
    }
  ],
  "maxPagesPerSite": 1,
  "renderJavaScript": false
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `cookies` (type: `string`):

No description

## `trackers` (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 = {
    "startUrls": [
        {
            "url": "https://apify.com"
        },
        {
            "url": "https://crawlee.dev"
        }
    ],
    "maxPagesPerSite": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("offerastudio/cookie-tracker-audit").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 = {
    "startUrls": [
        { "url": "https://apify.com" },
        { "url": "https://crawlee.dev" },
    ],
    "maxPagesPerSite": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("offerastudio/cookie-tracker-audit").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 '{
  "startUrls": [
    {
      "url": "https://apify.com"
    },
    {
      "url": "https://crawlee.dev"
    }
  ],
  "maxPagesPerSite": 1
}' |
apify call offerastudio/cookie-tracker-audit --silent --output-dataset

```

## MCP server setup

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

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/8TQKpizWTBr8XiPhN/builds/8jjXo6DuLrbceAFgP/openapi.json
