# Québec Bill 96 Website Auditor (Loi 96 French Compliance) (`verif96/quebec-bill-96-website-auditor`) Actor

Audit any website for Québec French-language compliance (Bill 96). Get a 0–100 score, English-only pages, English buttons on French pages, and a clear verdict. Bulk URLs, FR/EN reports.

- **URL**: https://apify.com/verif96/quebec-bill-96-website-auditor.md
- **Developed by:** [Manuarii WONG](https://apify.com/verif96) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 website 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

## Québec Bill 96 Website Auditor — French Language Compliance Checker

**Find out in seconds whether a website respects Québec's French-language rules (Bill 96 / Loi 96, Charter of the French Language).**

Paste one or hundreds of website URLs. The Actor crawls each site, detects the language of every page, and returns a **0–100 compliance score**, a clear verdict and the exact list of problems: English-only pages, missing French versions, English buttons and menus on French pages, and more.

> 🇫🇷 **En bref :** collez des adresses de sites web, l'Actor analyse chaque site et vous donne un score de conformité à la Loi 96 sur 100, un verdict et la liste précise des pages et boutons à traduire. Rapports en français ou en anglais.

### Who is it for?

- **Web agencies & freelancers**: audit a client's site before a redesign, or prospect Québec businesses whose sites need work.
- **Translation agencies**: find companies with English-only pages and send them a precise, data-backed quote.
- **Compliance, legal and marketing teams**: monitor your own websites (and your franchisees') on a schedule.
- **E-commerce brands selling in Québec**: check that product pages, cart and checkout are available in French.

### What it checks

| Check | Why it matters |
|---|---|
| French version available | Commercial websites aimed at Québec must be available in French. |
| English-only pages | English pages with no French equivalent (no `hreflang="fr"` and no language switcher link). |
| Homepage default language | Whether visitors land on English first. |
| English UI on French pages | Buttons, menus and form fields such as "Add to cart", "Sign in", "Learn more" left in English. |
| Mixed-language pages | Pages where French and English are mixed in the main content. |
| `lang` attribute | HTML `lang` missing or inconsistent with the actual content language. |
| Language switcher | Bilingual site without a visible way to switch to French. |

### Output example

​`json
{
  "domain": "example.ca",
  "score": 62,
  "verdict": "needs_review",
  "verdictLabel": "À revoir",
  "summary": "4 page(s) sur 15 sont uniquement en anglais, sans version française détectée. 3 page(s) en français contiennent des boutons, menus ou champs en anglais.",
  "pagesAudited": 15,
  "frenchPages": 10,
  "englishPages": 4,
  "mixedPages": 1,
  "englishOnlyPagesCount": 4,
  "homepageLanguage": "fr",
  "hasFrenchVersion": true,
  "hasLanguageSwitcher": true,
  "issues": [
    {
      "severity": "high",
      "code": "ENGLISH_ONLY_PAGES",
      "message": "4 page(s) sur 15 sont uniquement en anglais, sans version française détectée.",
      "examples": ["https://example.ca/blog/our-story", "https://example.ca/careers"]
    },
    {
      "severity": "medium",
      "code": "ENGLISH_UI_ON_FRENCH_PAGES",
      "message": "3 page(s) en français contiennent des boutons, menus ou champs en anglais.",
      "examples": ["Add to cart", "Sign in", "Learn more"]
    }
  ],
  "englishOnlyPages": ["https://example.ca/blog/our-story", "https://example.ca/careers"],
  "auditedAt": "2026-10-21T12:00:00.000Z"
}
​`

Verdicts: `likely_compliant` (score ≥ 85), `needs_review` (60–84), `likely_non_compliant` (< 60), `unreachable`.

### How to use

1. Add the websites to audit in **Websites to audit** (homepage URLs; `example.ca` without `https://` also works).
2. Choose how many pages to crawl per site (15 is a good default, up to 200).
3. Pick the report language (Français / English).
4. Run, then export results as JSON, CSV, Excel or via API.

💡 **Tip — lead generation:** run a Google Maps or business-directory Actor to collect Québec business websites, then feed the URLs into this auditor and keep the sites with a low score.

💡 **Tip — monitoring:** create a Schedule (e.g. monthly) and connect an integration (email, Slack, Google Sheets, Make, Zapier) to be alerted when a score drops.

### Pricing

Pay only for what you use: you are charged **per website successfully audited**. Unreachable or bot-protected websites are not charged.

### How it works

- Fast HTTP crawler (no browser), follows internal links of each site, prioritizing declared French versions (`hreflang`, language switcher).
- Language detection runs on every text block (headings, paragraphs, lists, tables, buttons), weighted by number of words.
- Short interface labels are matched against a dictionary of common English UI phrases.

### Limitations

- This is an **automated indicator, not legal advice**. Final compliance assessment belongs to the OQLF and legal professionals.
- Trademarks, proper names, images, videos and PDFs are not evaluated.
- Pages rendered only with JavaScript (some single-page apps) may show little text; the score is then based on fewer signals.
- The crawler respects normal crawl rates; very large sites are sampled (up to `maxPagesPerSite` pages).
- Websites that block bots (HTTP 403) are not bypassed: they are reported as not audited and are not charged.

### Responsible use

- Only public pages are read; no login, no personal data is collected — only language statistics.
- **Anti-spam laws:** if you use these reports for prospecting, you are responsible for complying with Canada's Anti-Spam Legislation (CASL / LCAP) and similar laws. Do not send unsolicited commercial emails without the required consent.
- Do not present results as an official or legal certification, and do not imply any endorsement by the OQLF.

### FAQ

**Is it legal to audit any website?** The Actor only reads public pages, like a search engine, at a polite rate.

**Does a score of 100 mean my site is compliant?** It means no issue was detected on the pages analysed. Use it as a strong first check, not as a legal certificate.

**Can I get page-by-page results?** Yes, enable **Include page-by-page details**.

# Actor input Schema

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

Homepage URLs of the websites to audit (one report per website). You can paste hundreds of URLs or upload a list.

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

How many pages to crawl on each website. More pages = more accurate audit, but slower.

## `reportLanguage` (type: `string`):

Language of the verdict, summary and issue messages.

## `includePageDetails` (type: `boolean`):

Adds the language analysis of every crawled page to each report (useful for agencies preparing a quote).

## `proxyConfiguration` (type: `object`):

The default Apify proxy works for most websites. Websites that block bots are reported as not audited and are not charged.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.quebec.ca"
    }
  ],
  "maxPagesPerSite": 15,
  "reportLanguage": "fr",
  "includePageDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `reports` (type: `string`):

One item per website audited.

# 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://www.quebec.ca"
        }
    ],
    "maxPagesPerSite": 15,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("verif96/quebec-bill-96-website-auditor").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://www.quebec.ca" }],
    "maxPagesPerSite": 15,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("verif96/quebec-bill-96-website-auditor").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://www.quebec.ca"
    }
  ],
  "maxPagesPerSite": 15,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call verif96/quebec-bill-96-website-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,verif96/quebec-bill-96-website-auditor"
        }
    }
}
```

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/diyTbghRJTFtJhGvc/builds/LR5vGenfTUuRCh5g1/openapi.json
