# Audit Loi 25 Québec : témoins (Quebec Law 25 Cookie Audit) (`loi25-audit/audit-loi25`) Actor

Témoins et traceurs avant consentement, bannière, politique de confidentialité : rapport en français avec capture et corrections. EN: Quebec Law 25 cookie & tracker audit.

- **URL**: https://apify.com/loi25-audit/audit-loi25.md
- **Developed by:** [Vigie Numérique](https://apify.com/loi25-audit) (community)
- **Categories:** Developer tools, SEO tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $40.00 / 1,000 site analysé

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

## Audit Loi 25 Québec : témoins et traceurs (Quebec Law 25 Cookie Audit)

*Un outil de Vigie Numérique.*

> **Ce rapport n’est pas un avis juridique.** Cet Actor produit une analyse technique automatisée. Consultez un professionnel qualifié pour évaluer vos obligations au titre de la Loi 25.

### Que fait cet Actor ?

**Audit Loi 25** visite la page d’accueil de sites web québécois **comme un nouveau visiteur qui n’a encore rien accepté**, puis vérifie :

1. **les témoins (cookies) et traceurs déposés avant tout consentement** : Google Analytics, Meta Pixel, Google Ads, LinkedIn, TikTok, Microsoft Clarity, Hotjar, StackAdapt et plus de 30 autres services connus ;
2. **la présence d’une bannière de consentement**, y compris dans un iframe : outil utilisé (OneTrust, Cookiebot, Axeptio, Didomi, Complianz, CookieYes, BigID, etc.) et présence de boutons « Accepter », « Refuser » (ou équivalent, comme « Nécessaires seulement ») et « Paramètres » ;
3. **la présence d’un lien vers la politique de confidentialité**.

Il produit ensuite un **rapport en français** (JSON + HTML), avec un **score sur 100**, un **indice de risque** et des **corrections suggérées** pour chaque problème. Une **capture d’écran** de la partie visible de chaque page est jointe.

### Pourquoi cet outil

- **Pensé pour la Loi 25.** Depuis septembre 2023, la Loi 25 (*Loi modernisant des dispositions législatives en matière de protection des renseignements personnels*) encadre l’usage des technologies qui permettent d’identifier, de localiser ou de profiler une personne. L’outil cible exactement ce qu’on peut vérifier de l’extérieur : ce qui se passe **avant** que le visiteur fasse un choix.
- **Un rapport en français, prêt à transmettre.** Un rapport HTML lisible par une personne non technique : sommaire de tous les sites, puis une fiche par site, avec la date et l’heure de l’analyse.
- **Une capture d’écran comme preuve.** Chaque fiche montre la partie visible de la page d’accueil telle qu’un nouveau visiteur la voit, bannière comprise.
- **Des corrections suggérées.** Chaque problème est accompagné d’une piste concrète. Par exemple : activer Google Consent Mode v2 en mode « refusé » par défaut, ou ajouter un bouton « Tout refuser » aussi visible que « Tout accepter ».
- **Une méthode transparente.** Pas de faux navigateur, blocages anti-robot respectés, robots.txt respecté (voir [Méthode](#méthode)).

#### Pour qui ?

- **Agences web et pigistes** : vérifier les sites de leurs clients avant et après une mise en ligne.
- **PME et OBNL** : faire un premier diagnostic de leur propre site.
- **Responsables de la protection des renseignements personnels** : suivre plusieurs sites au fil du temps, avec des exécutions planifiées.

### Combien ça coûte ?

**0,04 $ US par site analysé avec succès** (tarification à l’événement, *pay-per-event*).

- **Aucun frais** pour un site en erreur, un site qui bloque les robots (403, CAPTCHA) ou une page interdite par `robots.txt`.
- **Plafond respecté** : si vous fixez un montant maximal pour l’exécution, l’outil s’arrête proprement avant de le dépasser. Les sites restants sont signalés « non analysée (plafond de dépense atteint) » dans le rapport, et ne sont pas facturés.
- Exemple : 100 sites analysés avec succès coûtent 4 $ US.

### Comment l’utiliser ?

1. Collez la liste des URL à analyser dans **Sites web à auditer**.
2. Cliquez sur **Démarrer**.
3. Ouvrez l’onglet **Output** : le **Rapport HTML** se lit directement dans le navigateur, et les **Résultats (JSON)** sont exportables en JSON, CSV ou Excel.

L’analyse prend généralement de 15 à 30 secondes par site.

### Entrée

| Champ | Description | Par défaut |
|---|---|---|
| `startUrls` | Liste des sites à analyser (page d’accueil). `https://` est ajouté si absent. | — |
| `waitSecs` | Temps d’observation après le chargement, sans interaction. | `5` |
| `maxConcurrency` | Nombre de sites analysés en parallèle (2 recommandé avec 8 Go de mémoire). | `2` |
| `proxyCountry` | Facultatif : pays des adresses IP sur la plateforme Apify (code ISO à deux lettres, ex. `CA`). Si ce pays n’est pas disponible sur votre compte, l’exécution s’arrête après une seule tentative, avec un message clair. Non utilisé en exécution locale. | vide (aucun proxy) |
| `maxPages` | Réservé à une version future. Seule la page d’accueil est analysée pour l’instant. | `1` |

Exemple d’entrée prêt à lancer (le formulaire est déjà prérempli avec ce site de démonstration) :

```json
{
  "startUrls": [{ "url": "https://example.com" }],
  "waitSecs": 5,
  "maxConcurrency": 2,
  "proxyCountry": ""
}
```

### Sortie

#### Rapport HTML

Le **rapport HTML** est enregistré dans le Key-Value Store sous la clé `RAPPORT`. Il comprend :

- la **date et l’heure de l’analyse** (heure de l’Est), pour l’ensemble de l’exécution et pour chaque site ;
- un sommaire de tous les sites ;
- pour chaque site, une fiche avec le score, l’indice de risque, les problèmes et les corrections suggérées, la liste des traceurs et des témoins, ainsi que la capture d’écran.

Les captures d’écran sont enregistrées sous les clés `capture-…`.

#### Résultats JSON (dataset)

Le dataset contient une ligne par site. Exemple abrégé :

```json
{
  "url": "https://example.com",
  "domain": "example.com",
  "auditedAt": "2026-11-07T08:26:58.000Z",
  "status": "ok",
  "score": 35,
  "riskIndex": "Élevé",
  "trackerCount": 3,
  "trackingCookieCount": 4,
  "trackersBeforeConsent": [
    { "name": "Meta Pixel (Facebook)", "company": "Meta", "categoryLabel": "Publicité / ciblage", "requestCount": 2 }
  ],
  "cookiesBeforeConsent": [
    { "name": "_ga", "domain": ".example.com", "thirdParty": false, "trackingService": "Google Analytics", "lifetimeDays": 400 }
  ],
  "consentBanner": { "detected": true, "visible": true, "cmp": "Cookiebot", "hasRejectButton": false, "hasAcceptButton": true },
  "privacyPolicy": { "found": true, "url": "https://example.com/politique-de-confidentialite", "inFooter": true },
  "issues": [
    {
      "severity": "élevée",
      "problem": "Meta Pixel (Facebook) (Meta, publicité / ciblage) est actif avant tout consentement (2 requêtes).",
      "correction": "Ne charger le Pixel Meta qu’après le consentement…"
    }
  ],
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/…/records/capture-001-example.com",
  "disclaimer": "Ce rapport n’est pas un avis juridique. …"
}
```

Le champ `auditedAt` donne la date et l’heure de l’analyse de chaque site (format ISO 8601, en UTC).

Les **valeurs** des témoins ne sont jamais enregistrées, seulement leur nom, leur domaine et leur durée.

### Score et indice de risque

Chaque site part de **100 points**. Le score ne descend **jamais sous 0**.

| Constat | Points retirés |
|---|---|
| Traceur publicitaire, de mesure d’audience ou d’enregistrement de session actif avant consentement | −15 chacun (−5 si Google Consent Mode indique un refus par défaut) |
| Conteneur ou chargeur de balises (Google Tag Manager, gtag.js) chargé | 0 (informatif ; seules les balises de suivi qui envoient réellement des données sont pénalisées) |
| Témoin de suivi déposé avant consentement | −5 chacun (maximum −25) |
| Aucune bannière visible alors que des traceurs ou témoins de suivi sont actifs | −20 |
| Aucune bannière, et aucun traceur ni témoin de suivi détecté | 0 (informatif) |
| Bannière qui demande le consentement sans bouton « Refuser » ni équivalent (ex. « Nécessaires seulement ») | −10 |
| Aucun lien vers la politique de confidentialité | −20 |

| Score | Indice de risque |
|---|---|
| 85 à 100 | Faible |
| 60 à 84 | Modéré |
| 30 à 59 | Élevé |
| 0 à 29 | Critique |

L’**indice de risque** indique la quantité d’éléments techniques à examiner. Ce n’est **ni une attestation ni une évaluation de conformité**.

### Méthode

- Chaque site est ouvert dans un **navigateur Chrome neuf** (aucun témoin préexistant), en **français (fr-CA)**, avec le fuseau horaire de l’Est.
- L’Actor **ne clique jamais** sur la bannière : il observe seulement ce qui se passe avant tout choix du visiteur.
- Toutes les requêtes réseau sont comparées à une liste de traceurs connus. Les témoins sont lus directement dans le navigateur.
- Par défaut, aucun proxy n’est utilisé. Vous pouvez demander un proxy d’un pays précis (option `proxyCountry`), s’il est disponible sur votre compte Apify.
- Mémoire recommandée : **8 Go** (valeur par défaut), avec 2 sites analysés en parallèle.

#### Comment l’Actor se présente aux sites (transparence)

- **Aucune empreinte simulée** : l’Actor n’invente ni écran, ni matériel, ni polices.
- **User-agent Chrome standard et exact** : la vraie version de Chrome et le vrai système d’exploitation, sans la mention « Headless ».
- **Une seule exception, assumée** : le marqueur technique `navigator.webdriver`, qui signale un navigateur automatisé, est masqué. Certains sites **cachent leur bannière de consentement aux robots**. Nous l’avons constaté sur un site de référence, à deux reprises. Sans ce réglage, l’audit ne verrait pas la même page qu’un visiteur réel et pourrait se tromper. Ce réglage ne sert jamais à franchir un blocage.
- **Blocages respectés** : un code 401, 403 ou 429, un CAPTCHA ou une page de défi anti-robot (Cloudflare, DataDome, etc.) mettent fin à l’analyse du site. Il n’y a aucune nouvelle tentative, aucun changement d’adresse IP et aucune résolution de CAPTCHA. Le site est signalé « erreur (blocage anti-robot) ».
- **robots.txt respecté** : si le fichier `robots.txt` du site interdit la page d’accueil aux robots (règle `User-agent: *`), la page n’est pas chargée du tout. Elle est signalée « non analysée (robots.txt) » et **n’est pas facturée**.

### Limites

- **Sans proxy, l’analyse est faite depuis un serveur situé hors du Canada** (les serveurs de la plateforme Apify, généralement aux États-Unis). Certains sites affichent une bannière ou des traceurs différents selon le pays du visiteur, et le résultat peut alors différer de ce que voit un visiteur au Québec.
- Seule la **page d’accueil** est analysée.
- Les résultats reflètent l’état du site **à la date et à l’heure de l’analyse**. **Sur les sites avec publicité** (médias, sites de nouvelles, etc.), les annonces et les balises publicitaires changent souvent d’une visite à l’autre. Les traceurs et témoins observés, et donc le score, peuvent varier légèrement entre deux analyses du même site.
- La détection repose sur une **liste de traceurs connus** et sur des **heuristiques**. Un traceur hébergé sur le domaine du site (ex. marquage côté serveur) ou une bannière très atypique peuvent échapper à l’analyse.
- Le **contenu** de la politique de confidentialité n’est pas évalué.
- Certains sites bloquent les navigateurs automatisés (code 403, CAPTCHA, page de défi) ou les interdisent dans `robots.txt`. Ces sites ne peuvent pas être évalués par l’Actor (`blockedByAntiBot: true` ou `status: "non analysée"`) et doivent être vérifiés manuellement.

### Questions fréquentes

**Un score de 100 veut-il dire que mon site est conforme ?**
Non. Le score indique seulement qu’aucun des problèmes techniques recherchés n’a été détecté. La conformité dépend aussi de vos pratiques, de vos politiques et de vos contrats.

**Pourquoi Google Tag Manager est-il signalé ?**
GTM est un conteneur de balises : il ne trace pas lui-même. Il est donc mentionné à titre informatif, sans retirer de points. Seules les balises de suivi qu’il déclenche réellement avant le consentement (Google Analytics, Google Ads, Pixel Meta, etc.) sont pénalisées.

**Pourquoi le score d’un même site change-t-il entre deux analyses ?**
Sur les sites avec publicité, les balises publicitaires chargées dépendent des annonces affichées à ce moment-là. Comparez plutôt la tendance sur plusieurs analyses, en vous fiant à la date et à l’heure indiquées dans chaque rapport.

**Puis-je planifier l’Actor ?**
Oui. Utilisez les *Schedules* d’Apify pour relancer l’audit chaque semaine ou chaque mois.

***

### English summary

**Quebec Law 25 Cookie Audit** checks the homepage of Québec websites for cookies and trackers loaded **before any consent** (Google Analytics, Meta Pixel, Google Ads, LinkedIn, TikTok, Clarity, Hotjar and more). It also checks for a cookie consent banner, including whether it offers a reject button, and for a privacy policy link.

- **Output:** a French-language report (JSON dataset + HTML report in the key-value store) with a 0–100 score, a risk index, suggested fixes, the date and time of the analysis, and an above-the-fold screenshot.
- **Pricing:** USD 0.04 per successfully analysed site. Errors, anti-bot blocks and pages disallowed by robots.txt are never charged, and your spending limit is respected.
- **Method:** no fake fingerprints, a real Chrome user-agent, anti-bot blocks and robots.txt respected.
- On ad-supported sites, results can vary slightly from one visit to the next.

**This report is not legal advice.**

# Actor input Schema

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

Liste des URL à analyser (la page d'accueil de chaque site). Si le protocole manque, https:// est ajouté.

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

Temps d'attente après le chargement de la page, sans aucune interaction, pour laisser les traceurs se déclencher.

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

Nombre maximal de navigateurs ouverts en même temps. 2 est recommandé avec 8 Go de mémoire (environ 2 cœurs de processeur sur Apify).

## `proxyCountry` (type: `string`):

Facultatif. Code pays à deux lettres (ISO 3166-1, ex. CA) des adresses IP utilisées sur la plateforme Apify, pour voir le site comme un visiteur de ce pays. Vide par défaut : aucun proxy. Si aucune adresse IP de ce pays n’est disponible sur votre compte, l’exécution s’arrête après une seule tentative, avec un message clair. Ignoré en exécution locale.

## `maxPages` (type: `integer`):

Réservé à une version future. Pour l'instant, seule la page d'accueil est analysée (valeur 1).

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com"
    }
  ],
  "waitSecs": 5,
  "maxConcurrency": 2,
  "proxyCountry": "",
  "maxPages": 1
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `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 = {
    "startUrls": [
        {
            "url": "https://example.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("loi25-audit/audit-loi25").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://example.com" }] }

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

```

## MCP server setup

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

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/2cbV4ssih6nqwPw2A/builds/aOZo6dvJHYI1r2259/openapi.json
