Cookie Consent Audit
Pricing
from $42.50 / 1,000 domain auditeds
Cookie Consent Audit
Check any website for cookie consent problems from an EU address. One row per domain: consent platform, reject button on the first layer, cookies and trackers fired before consent and after reject, and Global Privacy Control response. Signals for privacy teams and DPOs.
Pricing
from $42.50 / 1,000 domain auditeds
Rating
0.0
(0)
Developer
Mamba Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 hours ago
Last modified
Categories
Share
Check any website for cookie consent problems the way an EU visitor meets them. The actor loads each domain from an EU address in fresh browser sessions: once before any consent click, once after clicking the reject button, and once with Global Privacy Control on. You get one flat row per domain with the consent platform it found, whether a reject button sits on the first layer of the banner, the tracking cookies and tracker requests that fired before consent and after the reject, and how the site responds to Global Privacy Control.
Built for privacy teams, data protection officers, and the agencies that audit sites for them. The output is a list of signals for a human to review. It is not a legal finding.
Features
- An EU vantage point. Banners change by visitor region, so a US address audits the wrong banner. In every browser session, the page itself and every request that decides the banner (the consent platform, Google tags, and consent hosts) go through the Apify proxy in the EU country you pick (Germany by default). Other requests, such as tracker calls and static files, are fetched directly and handed to the browser with its own cookies, so they are still seen and recorded. If no address in that country is available, the actor does not audit the domain from somewhere else; the row says so and is free.
- Three fresh sessions per domain. No cookie or browser storage carries from one session to the next: before consent, after reject, and with Global Privacy Control on.
- Before consent. Every cookie set and every request sent to a known tracker host before anyone clicks, with vendor and category (analytics, advertising, session replay, marketing automation).
- First layer controls. Whether accept and reject (or "necessary only") controls are visible on the first layer of the banner, without opening settings. The actor reads controls inside open shadow roots and frames, and knows the selectors of 17 consent platforms plus accept and reject wording in 16 languages.
- After reject. The actor clicks the reject control, reloads the page, and records the tracker requests and new tracking cookies that still appear.
- Consent platform detection. IAB TCF (
__tcfapi, with the CMP ID andgdprAppliesfrom a TCF ping), OneTrust, Cookiebot, Usercentrics, Didomi, TrustArc, Quantcast Choice, Sourcepoint, iubenda, Complianz, CookieYes, Osano, Termly, Axeptio, consentmanager, Borlabs Cookie, Klaro, and Google Funding Choices. - Google Consent Mode. Cookieless Consent Mode pings sent with ad and analytics storage denied (
gcs=G100) are counted on their own, so you can judge them apart from full tracking. - Global Privacy Control. A session that sends
Sec-GPC: 1and exposesnavigator.globalPrivacyControl, compared with the plain session, plus a read of/.well-known/gpc.json. - A fixed, versioned tracker list. 173 tracker hosts and 83 tracker cookie patterns ship in the actor, and every row names the list version it was checked against, so two audits are comparable.
- No personal data out. Cookie names and domains only, never cookie values.
Input
| Field | Type | Description |
|---|---|---|
domain | string | The website to audit, for example example.de or https://www.example.de/. The homepage is audited. |
domains | array | More domains or URLs in the same run. Duplicates are skipped. Up to 500 per run. |
proxy_country | select | The EU country the browser connects from: DE, FR, NL, IE, IT, ES, SE, PL, AT, BE, DK, FI, or none. Default DE. none connects without a proxy, which is not an EU vantage point. |
check_gpc | boolean | Also run the Global Privacy Control session and read /.well-known/gpc.json. Default true. |
max_charge_usd | number | Stop before the audit charges would pass this amount. |
skip_cache | boolean | A domain audited in the last 24 hours with the same country and GPC setting comes back from cache at no charge. Turn this on to audit it again. Default false. |
Output
One row per domain. Every row carries every field, with null where a value was not measured.
| Field | Type | Example |
|---|---|---|
domain, url_audited, final_url | string | example.de |
vantage_country, proxy_used, proxy_group | string, boolean, string | DE, true, residential |
render_status | string | rendered, blocked, timeout, unreachable, robots_disallowed |
cmp_detected, cmp_vendor, cmp_vendors_all | boolean, string, string | true, onetrust, onetrust |
tcf_api_present, tcf_cmp_id, tcf_gdpr_applies | boolean, integer, boolean | true, 28, true |
banner_detected, accept_on_first_layer, reject_on_first_layer | boolean | true, true, false |
reject_control_text | string | Alle ablehnen |
cookies_before_consent, non_essential_cookies_before_consent | integer | 14, 6 |
non_essential_cookies_before_consent_json | string (JSON list of name, domain, vendor, category) | |
tracker_requests_before_consent, consent_mode_denied_pings_before_consent | integer | 23, 0 |
tracker_hosts_before_consent_json, tracker_vendors_before_consent | string | `Google Analytics |
reject_clicked | boolean | true |
tracker_requests_after_reject, consent_mode_denied_pings_after_reject, tracker_hosts_after_reject_json | integer, integer, string | 0, 2 |
non_essential_cookies_after_reject, non_essential_cookies_after_reject_json | integer, string | 0 |
gpc_checked, gpc_tracker_requests, gpc_non_essential_cookies, gpc_reduces_tracking | boolean, integer, integer, boolean | true, 20, 6, false |
gpc_well_known_found, gpc_well_known_gpc | boolean | false, null |
signals, signals_count | string, integer | `trackers_before_consent |
tracker_list_version | string | 2026-10-19.1 |
from_cache, duration_ms | boolean, integer | false, 41000 |
row_status, error_reason | string | ok, null |
billable_events_json, checked_at | string | ["domain-audited"] |
The signals are: trackers_before_consent, non_essential_cookies_before_consent, no_reject_on_first_layer, trackers_after_reject, non_essential_cookies_after_reject, gpc_no_change, no_consent_banner_detected, and tcf_gdpr_applies_false_from_eu.
What the signals check, and the rules behind them
- ePrivacy Directive 2002/58/EC, Article 5(3). Storing or reading information on a visitor's device needs consent unless it is strictly necessary. The before consent fields show what a site stores and sends before any choice.
- GDPR, Article 4(11) and Article 7. Consent must be freely given, specific, informed, and unambiguous, and as easy to withdraw as to give. The after reject fields show whether a refusal is honored.
- EDPB Cookie Banner Taskforce report, 2023-01-18. The taskforce lists practices national regulators treat as problems, among them no reject option on the first layer and non essential cookies set before consent.
reject_on_first_layerand the before consent counts map to those practices. - Global Privacy Control. The GPC specification (w3c.github.io/gpc) defines the
Sec-GPCheader and/.well-known/gpc.json. The CCPA regulations treat it as an opt out preference signal for California. The actor reports whether the site sent fewer trackers with GPC on; it does not decide whether a law requires that.
Pricing
Pay per event. You pay for each domain that was fully audited, plus one small start fee per run.
| Event | Free plan | Starter (Bronze) | Scale (Silver) | Business (Gold) |
|---|---|---|---|---|
domain-audited, per audited domain | 0.05 USD (50.00 per 1,000) | 0.0475 USD (47.50 per 1,000) | 0.045 USD (45.00 per 1,000) | 0.0425 USD (42.50 per 1,000) |
apify-actor-start, per run | 0.001 USD | 0.001 USD | 0.001 USD | 0.001 USD |
- Only rows with
row_statusokare charged. Blocked, timed out, partial, unreachable, cached, and invalid input rows are free, and so are the rows of a run where no EU address was available; that run still pays the start fee. - Free plan cap: 30 audited domains a month on the Apify free plan (1.50 USD of evaluation). After that the run stops with a
cap_reachedrow. - The run stops at the maximum charge you set for it on Apify, and at
max_charge_usdwhen you set it.
Usage examples
Apify Console or API. One domain:
{ "domain": "example.de" }
A batch audited from France, without the GPC session:
{ "domains": ["example.fr", "example.de", "example.nl"], "proxy_country": "FR", "check_gpc": false }
A re-audit of a site you fixed today:
{ "domain": "example.de", "skip_cache": true }
Clay. Call the actor from an HTTP API column with your Apify token, one domain per row, {"domain": "{{Domain}}"}. Every input also works as a string, so "check_gpc": "false" is read the same as false. Map the flat output fields into columns.
MCP. The MCP server @mambalabsdev/mcp-cookie-consent-audit exposes the tool audit_cookie_consent to Claude, Cursor, and any MCP client.
Error handling
| Case | What you get |
|---|---|
| The site shows a challenge page (Cloudflare, Akamai, DataDome, and similar) | render_status blocked, row_status blocked, free |
| The page does not load within 30 seconds | render_status timeout, free |
| The domain does not resolve or refuses the connection | render_status unreachable, row_status error, free |
robots.txt disallows the whole site for every agent | render_status robots_disallowed, free |
| One of the sessions fails | row_status partial, with the measured fields filled, free |
| No proxy address in the chosen country | row_status error, nothing audited, free |
| The whole domain passes its 150 second budget | row_status timeout, free |
| Not a web domain | row_status invalid_input, free |
Limitations
- A row is one sample. Sites change their banner by device, time, and A/B test, and some show it only after a delay or a scroll.
- Automated checks cannot prove compliance. A clean row is not a statement that a site is lawful, and a signal is not a finding that it is not.
- Only the homepage is audited, on a desktop viewport.
- The tracker list is fixed and versioned. A tracker it does not list is not counted; the list grows with new versions, and every row names its version.
- Controls inside closed shadow roots, or banners drawn on a canvas, cannot be read.
- The GPC comparison counts requests. Ad requests vary from load to load, so a small difference is weak evidence.
- Strictly necessary cookies that the list does not recognize are counted in
cookies_before_consentbut not as tracking cookies.
Mamba Labs suite
Every actor in the suite takes a domain or a company and returns one flat row, so they stack in the same table.
Shared code: @mambalabsdev/gtm-core (output validation).
Sourcing and legal. The actor loads public web pages the way a visitor's browser does, with the page and its banner deciding requests sent through the Apify proxy. No login is used. It returns cookie names and request hosts, never cookie values or personal data. You are responsible for how you use the output.
Built by Mamba Labs.