[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fzilOLX2OAwnGwK64FQAwyTtljjq0-rKaDhLs4bwylK8":3,"$fJXChOiFtZQXaoodA0beyhvuINuVpl5lIrETmo7MR2s4":25},{"_id":4,"slug":5,"__v":6,"author":7,"body":8,"canonical":9,"category":10,"createdAt":11,"date":12,"description":13,"htmlContent":14,"image":15,"imageAlt":15,"readingTime":16,"tags":17,"title":23,"updatedAt":24},"69d038ee3c1c869cea8ff0e6","google-is-fixing-a-search-console-bug-that-inflated-impression-counts",0,"Equipe Seogard","Le 13 mai 2025, un bug de logging côté Google a commencé à gonfler artificiellement les compteurs d'impressions dans Search Console. Pendant près d'un an, des dizaines de milliers de sites ont vu leurs rapports de performance afficher des chiffres d'impressions supérieurs à la réalité — sans que le trafic réel ne suive. Google a confirmé le problème et annoncé un correctif progressif. Si vous avez fondé des décisions stratégiques sur ces données entre mai 2025 et aujourd'hui, il est temps de revoir vos analyses.\n\n## Ce qu'on sait du bug : anatomie d'une erreur de logging\n\nLe bug est documenté dans le [Data anomalies in Search Console](https://support.google.com/webmasters/answer/6211428) de Google. Il ne s'agit pas d'un problème d'indexation ou de crawl, mais d'une erreur dans le pipeline de collecte des données de performance — le système qui enregistre quand une URL apparaît dans les résultats de recherche.\n\n### Impressions vs. clics : pourquoi seules les impressions sont touchées\n\nUne impression dans Search Console est comptabilisée quand un lien vers votre site est rendu dans les résultats, que l'utilisateur scrolle jusqu'à le voir ou non (pour les résultats web classiques ; les règles diffèrent pour les images ou Discover). Le clic, lui, nécessite une action explicite de l'utilisateur — un événement distinct dans le pipeline de données.\n\nLe bug a affecté le compteur d'impressions parce qu'il se situe en amont dans la chaîne : c'est un log côté serveur Google qui enregistre \"cette URL a été incluse dans la SERP pour cette requête\". Une duplication dans ce logging — par exemple, un même affichage de SERP loggé deux fois à cause d'un retry ou d'un batch mal dédupliqué — gonfle les impressions sans affecter les clics.\n\nLe résultat observable : une **chute artificielle du CTR** sur les rapports de performance, puisque le dénominateur (impressions) était surévalué.\n\n### Chronologie et scope\n\n- **13 mai 2025** : début de l'anomalie dans les données.\n- **Mars 2026** : premiers signalements publics de la communauté SEO. Des rapports sur X et des forums spécialisés mentionnent des hausses d'impressions inexpliquées sans corrélation avec le trafic Analytics.\n- **Avril 2026** : Google confirme le bug et annonce un correctif progressif. Les données historiques seront corrigées rétroactivement dans les semaines suivantes.\n\nCela représente potentiellement **11 mois de données d'impressions biaisées**. Pour tout site qui utilise Search Console comme source de vérité pour le reporting SEO — ce qui est le cas de la majorité — c'est un problème structurel.\n\n## Quantifier l'impact : un scénario concret\n\nPrenons un cas réaliste : un site e-commerce de 12 000 pages produits positionné sur des requêtes longue traîne dans le secteur de l'ameublement.\n\n### Avant le bug (avril 2025)\n\n| Métrique | Valeur |\n|---|---|\n| Impressions mensuelles | 2,4 millions |\n| Clics mensuels | 96 000 |\n| CTR moyen | 4,0% |\n| Pages avec impressions > 0 | 8 200 |\n\n### Pendant le bug (janvier 2026)\n\n| Métrique | Valeur |\n|---|---|\n| Impressions mensuelles | 3,1 millions |\n| Clics mensuels | 98 000 |\n| CTR moyen | 3,16% |\n| Pages avec impressions > 0 | 9 400 |\n\nL'équipe SEO observe une hausse de 29% des impressions avec un trafic quasi stable. Deux interprétations possibles à ce moment-là :\n\n1. Google affiche plus le site mais les snippets ne convertissent pas → il faut optimiser les titles et meta descriptions.\n2. Les données sont fausses.\n\nSans connaissance du bug, la plupart des équipes choisissent l'interprétation 1. Résultat : des semaines de travail sur l'optimisation des snippets pour un problème qui n'existe pas. Pire, certaines équipes revoient leur stratégie de contenu en concluant que de nouvelles pages \"gagnent en visibilité\" alors qu'il s'agit d'impressions fantômes.\n\nC'est exactement le type de [régression silencieuse](/blog/regressions-seo-les-10-types-les-plus-frequents) qui consomme des ressources sans produire de résultat — parce que le problème est dans la mesure, pas dans le site.\n\n## Détecter l'anomalie dans vos propres données\n\nLa question immédiate : vos données sont-elles affectées, et à quel degré ? Voici comment le vérifier systématiquement.\n\n### Extraction via l'API Search Console\n\nL'interface web de Search Console ne permet pas d'exporter facilement des séries temporelles longues avec la granularité nécessaire. Passez par l'API. Voici un script Python qui extrait les impressions et clics quotidiens sur une période donnée et calcule le taux de variation par rapport à une baseline pré-bug :\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nDétection d'anomalies d'impressions Search Console.\nCompare la période post-bug (2025-05-13 à aujourd'hui)\navec une baseline pré-bug (3 mois avant).\n\"\"\"\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\nimport pandas as pd\nfrom datetime import datetime, timedelta\n\nSCOPES = ['https://www.googleapis.com/auth/webmasters.readonly']\nSERVICE_ACCOUNT_FILE = 'service-account-key.json'\nSITE_URL = 'sc-domain:votredomaine.fr'  # Propriété de domaine\n\n# Périodes de comparaison\nBASELINE_START = '2025-02-13'\nBASELINE_END = '2025-05-12'\nBUG_START = '2025-05-13'\nBUG_END = '2026-03-31'\n\ncredentials = service_account.Credentials.from_service_account_file(\n    SERVICE_ACCOUNT_FILE, scopes=SCOPES\n)\nservice = build('searchconsole', 'v1', credentials=credentials)\n\n\ndef fetch_daily_data(start_date: str, end_date: str) -> pd.DataFrame:\n    \"\"\"Récupère impressions et clics quotidiens.\"\"\"\n    rows = []\n    request_body = {\n        'startDate': start_date,\n        'endDate': end_date,\n        'dimensions': ['date'],\n        'rowLimit': 25000,\n        'dataState': 'all'\n    }\n    response = service.searchanalytics().query(\n        siteUrl=SITE_URL, body=request_body\n    ).execute()\n\n    for row in response.get('rows', []):\n        rows.append({\n            'date': row['keys'][0],\n            'clicks': row['clicks'],\n            'impressions': row['impressions'],\n            'ctr': row['ctr'],\n            'position': row['position']\n        })\n    return pd.DataFrame(rows)\n\n\nbaseline_df = fetch_daily_data(BASELINE_START, BASELINE_END)\nbug_df = fetch_daily_data(BUG_START, BUG_END)\n\n# Calcul des moyennes quotidiennes\nbaseline_avg_impressions = baseline_df['impressions'].mean()\nbaseline_avg_clicks = baseline_df['clicks'].mean()\nbaseline_avg_ctr = baseline_df['ctr'].mean()\n\nbug_avg_impressions = bug_df['impressions'].mean()\nbug_avg_clicks = bug_df['clicks'].mean()\nbug_avg_ctr = bug_df['ctr'].mean()\n\nimpression_delta = ((bug_avg_impressions - baseline_avg_impressions)\n                    / baseline_avg_impressions) * 100\nclick_delta = ((bug_avg_clicks - baseline_avg_clicks)\n               / baseline_avg_clicks) * 100\n\nprint(f\"Baseline impressions/jour : {baseline_avg_impressions:,.0f}\")\nprint(f\"Post-bug impressions/jour : {bug_avg_impressions:,.0f}\")\nprint(f\"Delta impressions : {impression_delta:+.1f}%\")\nprint(f\"\")\nprint(f\"Baseline clics/jour : {baseline_avg_clicks:,.0f}\")\nprint(f\"Post-bug clics/jour : {bug_avg_clicks:,.0f}\")\nprint(f\"Delta clics : {click_delta:+.1f}%\")\nprint(f\"\")\nprint(f\"Baseline CTR : {baseline_avg_ctr:.2%}\")\nprint(f\"Post-bug CTR : {bug_avg_ctr:.2%}\")\n\n# Flag si les impressions augmentent significativement\n# sans que les clics suivent\nif impression_delta > 15 and abs(click_delta) \u003C 5:\n    print(\"\\n⚠ ANOMALIE DÉTECTÉE : hausse d'impressions \"\n          \"sans corrélation clics. Probable impact du bug GSC.\")\n```\n\nLe signal clé : **une hausse significative d'impressions (>15%) avec des clics stables ou en légère progression**. Si les deux métriques évoluent dans des proportions similaires, l'augmentation est probablement organique.\n\n### Croisement avec les logs serveur\n\nLes données Search Console sont déclaratives (côté Google). Vos logs serveur sont factuels (côté vous). En croisant les deux, vous pouvez confirmer la divergence.\n\n```bash\n# Extraction du nombre de visites organiques Google par jour\n# depuis les access logs Nginx (format combined)\n# Filtre sur le referer Google et exclut les bots\n\nawk -F'\"' '\n  /google\\.(com|fr|de|co\\.uk)/ && !/Googlebot|AdsBot/ {\n    split($1, a, \" \")\n    split(a[4], d, \":\")\n    gsub(/\\[/, \"\", d[1])\n    dates[d[1]]++\n  }\n  END {\n    for (date in dates)\n      print date, dates[date]\n  }\n' /var/log/nginx/access.log.* | sort -k1 > google_organic_visits.tsv\n\n# Comparaison rapide avec les clics GSC exportés\npaste google_organic_visits.tsv gsc_clicks_export.tsv | \\\n  awk '{\n    diff = ($2 - $4) / $4 * 100;\n    printf \"%s\\tLogs: %d\\tGSC: %d\\tDelta: %.1f%%\\n\", $1, $2, $4, diff\n  }'\n```\n\nUn delta constant entre logs serveur et clics GSC confirme que les clics GSC restent fiables. C'est bien les impressions qui sont le problème.\n\nCette approche de validation croisée est exactement ce que recommande un [monitoring continu](/blog/monitoring-seo-pourquoi-les-audits-ponctuels-ne-suffisent-plus) : ne jamais se fier à une seule source de données.\n\n## Les effets en cascade sur vos workflows SEO\n\nLe problème ne se limite pas à des chiffres faux dans un rapport. Les impressions Search Console alimentent des dizaines de workflows en production.\n\n### Priorisation des optimisations on-page\n\nLa matrice classique CTR vs. position — où vous identifiez les requêtes à fort potentiel (bonne position, CTR faible) — est directement corrompue. Avec des impressions gonflées, le CTR calculé est mécaniquement plus bas. Des requêtes parfaitement performantes apparaissent comme sous-optimisées.\n\nSi vous avez utilisé cette matrice entre mai 2025 et maintenant pour prioriser des refontes de titles ou de meta descriptions, relancez l'analyse après la correction des données.\n\n### Reporting client et KPIs\n\nPour les agences et les consultants, les impressions sont un KPI de reporting standard. Une hausse de 25% des impressions, même sans clics supplémentaires, peut être présentée comme un signal positif (\"la visibilité augmente, les clics suivront\"). Ce narratif est faux si les impressions sont gonflées.\n\nRecommandation pragmatique : ajoutez une note dans tous les rapports couvrant la période mai 2025 – correction confirmée, indiquant que les données d'impressions GSC sont suspectes et seront révisées.\n\n### Modèles de forecasting\n\nSi votre modèle de prévision de trafic utilise les impressions GSC comme variable d'entrée — ce qui est courant dans les modèles de type `clicks = impressions × CTR_modélisé` — vos projections pour 2026 sont biaisées. Le modèle a appris un niveau d'impressions artificiellement élevé.\n\nAprès la correction, re-entraînez vos modèles sur les données corrigées. En attendant, basez vos forecasts sur les clics uniquement.\n\n## Automatiser la détection de ce type d'anomalie\n\nCe bug illustre un problème plus large : comment détecter une dérive dans des données tierces que vous ne contrôlez pas ?\n\n### Monitoring des ratios plutôt que des absolus\n\nLes valeurs absolues d'impressions peuvent varier pour mille raisons légitimes (saisonnalité, nouveau contenu, core update). Ce qui trahit une anomalie de données, c'est la **rupture dans les ratios**.\n\nLe ratio `clics/impressions` (CTR) est normalement stable à position constante. Si la position moyenne ne bouge pas, que les clics sont stables, mais que le CTR chute — c'est un signal fort d'anomalie côté impressions.\n\nVoici un check automatisable en TypeScript qui interroge l'API GSC et alerte quand le CTR dévie de plus de 2 écarts-types par rapport à la moyenne glissante :\n\n```typescript\n// ctr-anomaly-detector.ts\n// À exécuter en cron quotidien via GitHub Actions ou un scheduler\nimport { google } from 'googleapis';\n\ninterface DailyMetrics {\n  date: string;\n  impressions: number;\n  clicks: number;\n  ctr: number;\n  position: number;\n}\n\nasync function fetchMetrics(\n  auth: any,\n  siteUrl: string,\n  startDate: string,\n  endDate: string\n): Promise\u003CDailyMetrics[]> {\n  const searchconsole = google.searchconsole({ version: 'v1', auth });\n  const response = await searchconsole.searchanalytics.query({\n    siteUrl,\n    requestBody: {\n      startDate,\n      endDate,\n      dimensions: ['date'],\n      rowLimit: 25000,\n    },\n  });\n\n  return (response.data.rows || []).map((row) => ({\n    date: row.keys![0],\n    impressions: row.impressions!,\n    clicks: row.clicks!,\n    ctr: row.ctr!,\n    position: row.position!,\n  }));\n}\n\nfunction detectCtrAnomaly(\n  metrics: DailyMetrics[],\n  windowSize: number = 30,\n  threshold: number = 2.0\n): { date: string; ctr: number; expected: number; zScore: number }[] {\n  const anomalies: {\n    date: string; ctr: number; expected: number; zScore: number;\n  }[] = [];\n\n  for (let i = windowSize; i \u003C metrics.length; i++) {\n    const window = metrics.slice(i - windowSize, i);\n    const mean = window.reduce((s, m) => s + m.ctr, 0) / windowSize;\n    const stdDev = Math.sqrt(\n      window.reduce((s, m) => s + Math.pow(m.ctr - mean, 2), 0) / windowSize\n    );\n\n    if (stdDev === 0) continue;\n\n    const current = metrics[i];\n    const zScore = (current.ctr - mean) / stdDev;\n\n    // On cherche spécifiquement une chute du CTR (z-score négatif)\n    // combinée à une position stable\n    const avgPosition =\n      window.reduce((s, m) => s + m.position, 0) / windowSize;\n    const positionDrift = Math.abs(current.position - avgPosition);\n\n    if (zScore \u003C -threshold && positionDrift \u003C 1.0) {\n      anomalies.push({\n        date: current.date,\n        ctr: current.ctr,\n        expected: mean,\n        zScore,\n      });\n    }\n  }\n\n  return anomalies;\n}\n\n// Usage\nasync function main() {\n  const auth = new google.auth.GoogleAuth({\n    keyFile: 'service-account-key.json',\n    scopes: ['https://www.googleapis.com/auth/webmasters.readonly'],\n  });\n\n  const metrics = await fetchMetrics(\n    await auth.getClient(),\n    'sc-domain:votredomaine.fr',\n    '2025-01-01',\n    '2026-03-31'\n  );\n\n  const anomalies = detectCtrAnomaly(metrics);\n\n  if (anomalies.length > 0) {\n    console.error(`🔴 ${anomalies.length} anomalies CTR détectées :`);\n    anomalies.forEach((a) => {\n      console.error(\n        `  ${a.date}: CTR=${(a.ctr * 100).toFixed(2)}% ` +\n        `(attendu: ${(a.expected * 100).toFixed(2)}%, z=${a.zScore.toFixed(2)})`\n      );\n    });\n    process.exit(1); // Fail pour déclencher une alerte\n  } else {\n    console.log('✅ Pas d\\'anomalie CTR détectée.');\n  }\n}\n\nmain();\n```\n\nCe type de détection aurait signalé le problème dès la troisième semaine de mai 2025 — soit 10 mois avant la confirmation officielle de Google.\n\nUn outil comme Seogard, qui monitore les métriques SEO en continu et détecte les écarts statistiquement significatifs, permet d'automatiser ce type de surveillance sans maintenir des scripts maison. Le principe reste le même : surveiller les ratios, pas les absolus.\n\n### Intégration dans un pipeline CI/CD\n\nPour les équipes qui ont déjà des [checks SEO dans leur CI/CD](/blog/automatiser-les-checks-seo-dans-le-ci-cd), ajoutez un job scheduled (pas déclenché au deploy, mais en cron) qui exécute ce type de vérification :\n\n```yaml\n# .github/workflows/gsc-anomaly-check.yml\nname: GSC Anomaly Detection\non:\n  schedule:\n    - cron: '0 8 * * 1'  # Tous les lundis à 8h UTC\n  workflow_dispatch:\n\njobs:\n  check-ctr-anomalies:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: '20'\n\n      - run: npm ci\n\n      - name: Run CTR anomaly detection\n        env:\n          GOOGLE_SERVICE_ACCOUNT_KEY: ${{ secrets.GSC_SERVICE_ACCOUNT_KEY }}\n        run: npx tsx scripts/ctr-anomaly-detector.ts\n\n      - name: Notify on failure\n        if: failure()\n        uses: slackapi/slack-github-action@v1.27.0\n        with:\n          payload: |\n            {\n              \"text\": \"⚠️ Anomalie CTR détectée dans Search Console. Vérifiez les données d'impressions.\"\n            }\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}\n```\n\n## Ce que ce bug révèle sur la fiabilité des données Google\n\n### Search Console n'est pas une source de vérité absolue\n\nCe n'est pas la première anomalie de données dans Search Console. Google maintient une [liste des anomalies connues](https://support.google.com/webmasters/answer/6211428) qui documente des incidents récurrents — certains ayant duré plusieurs mois avant correction.\n\nLes données Search Console sont un **échantillon traité** de ce que Google observe, pas un reflet exhaustif et exact de la réalité. Les impressions en particulier dépendent de définitions complexes (qu'est-ce qu'une \"impression\" pour un résultat en position 8 sur mobile ? pour un carrousel d'images ? pour un résultat Discover ?). La [documentation officielle Google sur les impressions](https://support.google.com/webmasters/answer/7042828) détaille ces nuances.\n\n### La redondance des sources de données n'est pas optionnelle\n\nSi votre stack de reporting SEO repose à 100% sur l'API Search Console, vous avez un single point of failure sur la fiabilité des données. Croisez systématiquement avec :\n\n- **Logs serveur** : le referer Google dans vos access logs donne le volume réel de visites organiques. Pas les impressions, mais les clics — ce qui permet de valider la cohérence.\n- **Google Analytics / solution analytics côté client** : pour une deuxième mesure du trafic entrant.\n- **Outils de suivi de position tiers** (Semrush, Ahrefs, etc.) : si votre position moyenne GSC reste stable mais que le CTR chute, c'est un signal d'anomalie côté impressions.\n\nCe principe de triangulation est particulièrement critique quand vous interprétez les effets d'une [core update](/blog/google-begins-rolling-out-march-2026-core-update-via-sejournal-mattgsouthern) : il faut distinguer une variation réelle de performance d'un artefact de mesure.\n\n### L'impact sur les rapports GSC sous-exploités\n\nLe rapport Discover, le rapport Google News, le rapport sur les performances vidéo — ces [rapports souvent ignorés](/blog/google-search-console-les-rapports-que-vous-ignorez) de Search Console utilisent le même pipeline de logging. Il est probable que le bug les ait également affectés, même si Google n'a pas explicitement confirmé le scope exact.\n\nVérifiez vos données Discover avec la même méthodologie : si les impressions Discover ont bondi en mai 2025 sans que vos analytics ne confirment une hausse de trafic Discover, le bug est probablement en cause.\n\n## Actions concrètes : que faire maintenant\n\n### Court terme (cette semaine)\n\n1. **Identifiez l'amplitude du problème** sur votre site avec le script Python ci-dessus ou via l'export manuel de GSC (Performance → Export → Google Sheets).\n2. **Documentez l'anomalie** dans votre outil de reporting. Ajoutez une annotation sur la date du 13 mai 2025 dans Google Analytics et dans tout dashboard qui affiche des données GSC.\n3. **Gelez les décisions basées sur le CTR** calculé à partir des données GSC sur la période affectée. Ne lancez pas de chantier d'optimisation de snippets basé sur un CTR artificiellement bas.\n\n### Moyen terme (après le correctif Google)\n\n4. **Re-téléchargez les données corrigées** via l'API une fois le rollout du fix confirmé. Comparez avec vos exports pré-correction pour quantifier l'écart exact sur votre site.\n5. **Recalibrez vos modèles de forecasting** si vous utilisez les impressions comme variable d'entrée.\n6. **Auditez les décisions prises** entre mai 2025 et la correction. Y a-t-il des chantiers lancés sur la base de données fausses ?\n\n### Long terme (process)\n\n7. **Mettez en place un monitoring automatisé** des ratios GSC (CTR à position constante, ratio impressions/clics). Le script TypeScript ci-dessus est un point de départ.\n8. **Diversifiez vos sources de données** : ne faites jamais reposer un KPI stratégique sur une seule source, surtout une source que vous ne contrôlez pas.\n\nCe bug est un rappel brutal : les données tierces peuvent mentir silencieusement pendant des mois. Un [déploiement un vendredi soir](/blog/deploiement-vendredi-soir-comment-eviter-la-catastrophe-seo) peut casser votre SEO et vous le saurez vite. Un bug de logging côté Google peut biaiser votre stratégie pendant un an — et vous ne le saurez que quand Google décidera de le communiquer.\n\nLa seule défense durable, c'est la surveillance continue multi-sources, avec des alertes sur les écarts statistiques — pas sur les valeurs absolues. C'est exactement ce que des outils comme Seogard automatisent : détecter la dérive avant qu'elle ne devienne une erreur stratégique.","https://seogard.io/blog/google-is-fixing-a-search-console-bug-that-inflated-impression-counts","Actualités SEO","2026-04-03T22:02:22.423Z","2026-04-03","Google a surestimé les impressions dans Search Console depuis le 13 mai 2025. Analyse technique du bug, impact sur vos données et scripts pour détecter les anomalies.","\u003Cp>Le 13 mai 2025, un bug de logging côté Google a commencé à gonfler artificiellement les compteurs d'impressions dans Search Console. Pendant près d'un an, des dizaines de milliers de sites ont vu leurs rapports de performance afficher des chiffres d'impressions supérieurs à la réalité — sans que le trafic réel ne suive. Google a confirmé le problème et annoncé un correctif progressif. Si vous avez fondé des décisions stratégiques sur ces données entre mai 2025 et aujourd'hui, il est temps de revoir vos analyses.\u003C/p>\n\u003Ch2>Ce qu'on sait du bug : anatomie d'une erreur de logging\u003C/h2>\n\u003Cp>Le bug est documenté dans le \u003Ca href=\"https://support.google.com/webmasters/answer/6211428\">Data anomalies in Search Console\u003C/a> de Google. Il ne s'agit pas d'un problème d'indexation ou de crawl, mais d'une erreur dans le pipeline de collecte des données de performance — le système qui enregistre quand une URL apparaît dans les résultats de recherche.\u003C/p>\n\u003Ch3>Impressions vs. clics : pourquoi seules les impressions sont touchées\u003C/h3>\n\u003Cp>Une impression dans Search Console est comptabilisée quand un lien vers votre site est rendu dans les résultats, que l'utilisateur scrolle jusqu'à le voir ou non (pour les résultats web classiques ; les règles diffèrent pour les images ou Discover). Le clic, lui, nécessite une action explicite de l'utilisateur — un événement distinct dans le pipeline de données.\u003C/p>\n\u003Cp>Le bug a affecté le compteur d'impressions parce qu'il se situe en amont dans la chaîne : c'est un log côté serveur Google qui enregistre \"cette URL a été incluse dans la SERP pour cette requête\". Une duplication dans ce logging — par exemple, un même affichage de SERP loggé deux fois à cause d'un retry ou d'un batch mal dédupliqué — gonfle les impressions sans affecter les clics.\u003C/p>\n\u003Cp>Le résultat observable : une \u003Cstrong>chute artificielle du CTR\u003C/strong> sur les rapports de performance, puisque le dénominateur (impressions) était surévalué.\u003C/p>\n\u003Ch3>Chronologie et scope\u003C/h3>\n\u003Cul>\n\u003Cli>\u003Cstrong>13 mai 2025\u003C/strong> : début de l'anomalie dans les données.\u003C/li>\n\u003Cli>\u003Cstrong>Mars 2026\u003C/strong> : premiers signalements publics de la communauté SEO. Des rapports sur X et des forums spécialisés mentionnent des hausses d'impressions inexpliquées sans corrélation avec le trafic Analytics.\u003C/li>\n\u003Cli>\u003Cstrong>Avril 2026\u003C/strong> : Google confirme le bug et annonce un correctif progressif. Les données historiques seront corrigées rétroactivement dans les semaines suivantes.\u003C/li>\n\u003C/ul>\n\u003Cp>Cela représente potentiellement \u003Cstrong>11 mois de données d'impressions biaisées\u003C/strong>. Pour tout site qui utilise Search Console comme source de vérité pour le reporting SEO — ce qui est le cas de la majorité — c'est un problème structurel.\u003C/p>\n\u003Ch2>Quantifier l'impact : un scénario concret\u003C/h2>\n\u003Cp>Prenons un cas réaliste : un site e-commerce de 12 000 pages produits positionné sur des requêtes longue traîne dans le secteur de l'ameublement.\u003C/p>\n\u003Ch3>Avant le bug (avril 2025)\u003C/h3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Métrique\u003C/th>\n\u003Cth>Valeur\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Impressions mensuelles\u003C/td>\n\u003Ctd>2,4 millions\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Clics mensuels\u003C/td>\n\u003Ctd>96 000\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>CTR moyen\u003C/td>\n\u003Ctd>4,0%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Pages avec impressions > 0\u003C/td>\n\u003Ctd>8 200\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Ch3>Pendant le bug (janvier 2026)\u003C/h3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Métrique\u003C/th>\n\u003Cth>Valeur\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Impressions mensuelles\u003C/td>\n\u003Ctd>3,1 millions\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Clics mensuels\u003C/td>\n\u003Ctd>98 000\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>CTR moyen\u003C/td>\n\u003Ctd>3,16%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Pages avec impressions > 0\u003C/td>\n\u003Ctd>9 400\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cp>L'équipe SEO observe une hausse de 29% des impressions avec un trafic quasi stable. Deux interprétations possibles à ce moment-là :\u003C/p>\n\u003Col>\n\u003Cli>Google affiche plus le site mais les snippets ne convertissent pas → il faut optimiser les titles et meta descriptions.\u003C/li>\n\u003Cli>Les données sont fausses.\u003C/li>\n\u003C/ol>\n\u003Cp>Sans connaissance du bug, la plupart des équipes choisissent l'interprétation 1. Résultat : des semaines de travail sur l'optimisation des snippets pour un problème qui n'existe pas. Pire, certaines équipes revoient leur stratégie de contenu en concluant que de nouvelles pages \"gagnent en visibilité\" alors qu'il s'agit d'impressions fantômes.\u003C/p>\n\u003Cp>C'est exactement le type de \u003Ca href=\"/blog/regressions-seo-les-10-types-les-plus-frequents\">régression silencieuse\u003C/a> qui consomme des ressources sans produire de résultat — parce que le problème est dans la mesure, pas dans le site.\u003C/p>\n\u003Ch2>Détecter l'anomalie dans vos propres données\u003C/h2>\n\u003Cp>La question immédiate : vos données sont-elles affectées, et à quel degré ? Voici comment le vérifier systématiquement.\u003C/p>\n\u003Ch3>Extraction via l'API Search Console\u003C/h3>\n\u003Cp>L'interface web de Search Console ne permet pas d'exporter facilement des séries temporelles longues avec la granularité nécessaire. Passez par l'API. Voici un script Python qui extrait les impressions et clics quotidiens sur une période donnée et calcule le taux de variation par rapport à une baseline pré-bug :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">#!/usr/bin/env python3\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">\"\"\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">Détection d'anomalies d'impressions Search Console.\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">Compare la période post-bug (2025-05-13 à aujourd'hui)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">avec une baseline pré-bug (3 mois avant).\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">\"\"\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> google.oauth2 \u003C/span>\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> service_account\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> googleapiclient.discovery \u003C/span>\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> build\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> pandas \u003C/span>\u003Cspan style=\"color:#F97583\">as\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> pd\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> datetime \u003C/span>\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> datetime, timedelta\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">SCOPES\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'https://www.googleapis.com/auth/webmasters.readonly'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">SERVICE_ACCOUNT_FILE\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'service-account-key.json'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">SITE_URL\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'sc-domain:votredomaine.fr'\u003C/span>\u003Cspan style=\"color:#6A737D\">  # Propriété de domaine\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Périodes de comparaison\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">BASELINE_START\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '2025-02-13'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">BASELINE_END\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '2025-05-12'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">BUG_START\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '2025-05-13'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">BUG_END\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '2026-03-31'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">credentials \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> service_account.Credentials.from_service_account_file(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    SERVICE_ACCOUNT_FILE\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">scopes\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">SCOPES\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">service \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> build(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'searchconsole'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'v1'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">credentials\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">credentials)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">def\u003C/span>\u003Cspan style=\"color:#B392F0\"> fetch_daily_data\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(start_date: \u003C/span>\u003Cspan style=\"color:#79B8FF\">str\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, end_date: \u003C/span>\u003Cspan style=\"color:#79B8FF\">str\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) -> pd.DataFrame:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    \"\"\"Récupère impressions et clics quotidiens.\"\"\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    rows \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> []\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    request_body \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        'startDate'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: start_date,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        'endDate'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: end_date,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        'dimensions'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: [\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'date'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        'rowLimit'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#79B8FF\">25000\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        'dataState'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'all'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    response \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> service.searchanalytics().query(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">        siteUrl\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">SITE_URL\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">body\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">request_body\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    ).execute()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> row \u003C/span>\u003Cspan style=\"color:#F97583\">in\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> response.get(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'rows'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, []):\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        rows.append({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">            'date'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: row[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'keys'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">][\u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">            'clicks'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: row[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'clicks'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">            'impressions'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: row[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'impressions'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">            'ctr'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: row[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'ctr'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">            'position'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: row[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'position'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        })\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> pd.DataFrame(rows)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">baseline_df \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> fetch_daily_data(\u003C/span>\u003Cspan style=\"color:#79B8FF\">BASELINE_START\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">BASELINE_END\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">bug_df \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> fetch_daily_data(\u003C/span>\u003Cspan style=\"color:#79B8FF\">BUG_START\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">BUG_END\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Calcul des moyennes quotidiennes\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">baseline_avg_impressions \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> baseline_df[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'impressions'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].mean()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">baseline_avg_clicks \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> baseline_df[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'clicks'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].mean()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">baseline_avg_ctr \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> baseline_df[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'ctr'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].mean()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">bug_avg_impressions \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> bug_df[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'impressions'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].mean()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">bug_avg_clicks \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> bug_df[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'clicks'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].mean()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">bug_avg_ctr \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> bug_df[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'ctr'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">].mean()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">impression_delta \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ((bug_avg_impressions \u003C/span>\u003Cspan style=\"color:#F97583\">-\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> baseline_avg_impressions)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">                    /\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> baseline_avg_impressions) \u003C/span>\u003Cspan style=\"color:#F97583\">*\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 100\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">click_delta \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ((bug_avg_clicks \u003C/span>\u003Cspan style=\"color:#F97583\">-\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> baseline_avg_clicks)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">               /\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> baseline_avg_clicks) \u003C/span>\u003Cspan style=\"color:#F97583\">*\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 100\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Baseline impressions/jour : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">baseline_avg_impressions\u003C/span>\u003Cspan style=\"color:#F97583\">:,.0f\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Post-bug impressions/jour : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">bug_avg_impressions\u003C/span>\u003Cspan style=\"color:#F97583\">:,.0f\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Delta impressions : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">impression_delta\u003C/span>\u003Cspan style=\"color:#F97583\">:+.1f\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">%\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Baseline clics/jour : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">baseline_avg_clicks\u003C/span>\u003Cspan style=\"color:#F97583\">:,.0f\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Post-bug clics/jour : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">bug_avg_clicks\u003C/span>\u003Cspan style=\"color:#F97583\">:,.0f\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Delta clics : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">click_delta\u003C/span>\u003Cspan style=\"color:#F97583\">:+.1f\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">%\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Baseline CTR : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">baseline_avg_ctr\u003C/span>\u003Cspan style=\"color:#F97583\">:.2%\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Post-bug CTR : \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">bug_avg_ctr\u003C/span>\u003Cspan style=\"color:#F97583\">:.2%\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Flag si les impressions augmentent significativement\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># sans que les clics suivent\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> impression_delta \u003C/span>\u003Cspan style=\"color:#F97583\">>\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 15\u003C/span>\u003Cspan style=\"color:#F97583\"> and\u003C/span>\u003Cspan style=\"color:#79B8FF\"> abs\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(click_delta) \u003C/span>\u003Cspan style=\"color:#F97583\">&#x3C;\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 5\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\n\u003C/span>\u003Cspan style=\"color:#9ECBFF\">⚠ ANOMALIE DÉTECTÉE : hausse d'impressions \"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">          \"sans corrélation clics. Probable impact du bug GSC.\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Le signal clé : \u003Cstrong>une hausse significative d'impressions (>15%) avec des clics stables ou en légère progression\u003C/strong>. Si les deux métriques évoluent dans des proportions similaires, l'augmentation est probablement organique.\u003C/p>\n\u003Ch3>Croisement avec les logs serveur\u003C/h3>\n\u003Cp>Les données Search Console sont déclaratives (côté Google). Vos logs serveur sont factuels (côté vous). En croisant les deux, vous pouvez confirmer la divergence.\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Extraction du nombre de visites organiques Google par jour\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># depuis les access logs Nginx (format combined)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Filtre sur le referer Google et exclut les bots\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">awk\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -F\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'\"'\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  /google\\.(com|fr|de|co\\.uk)/ &#x26;&#x26; !/Googlebot|AdsBot/ {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    split($1, a, \" \")\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    split(a[4], d, \":\")\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    gsub(/\\[/, \"\", d[1])\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    dates[d[1]]++\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  END {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    for (date in dates)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">      print date, dates[date]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">'\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /var/log/nginx/access.log.\u003C/span>\u003Cspan style=\"color:#79B8FF\">*\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> sort\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -k1\u003C/span>\u003Cspan style=\"color:#F97583\"> >\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> google_organic_visits.tsv\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Comparaison rapide avec les clics GSC exportés\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">paste\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> google_organic_visits.tsv\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> gsc_clicks_export.tsv\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#79B8FF\"> \\\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">  awk\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    diff = ($2 - $4) / $4 * 100;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    printf \"%s\\tLogs: %d\\tGSC: %d\\tDelta: %.1f%%\\n\", $1, $2, $4, diff\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  }'\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Un delta constant entre logs serveur et clics GSC confirme que les clics GSC restent fiables. C'est bien les impressions qui sont le problème.\u003C/p>\n\u003Cp>Cette approche de validation croisée est exactement ce que recommande un \u003Ca href=\"/blog/monitoring-seo-pourquoi-les-audits-ponctuels-ne-suffisent-plus\">monitoring continu\u003C/a> : ne jamais se fier à une seule source de données.\u003C/p>\n\u003Ch2>Les effets en cascade sur vos workflows SEO\u003C/h2>\n\u003Cp>Le problème ne se limite pas à des chiffres faux dans un rapport. Les impressions Search Console alimentent des dizaines de workflows en production.\u003C/p>\n\u003Ch3>Priorisation des optimisations on-page\u003C/h3>\n\u003Cp>La matrice classique CTR vs. position — où vous identifiez les requêtes à fort potentiel (bonne position, CTR faible) — est directement corrompue. Avec des impressions gonflées, le CTR calculé est mécaniquement plus bas. Des requêtes parfaitement performantes apparaissent comme sous-optimisées.\u003C/p>\n\u003Cp>Si vous avez utilisé cette matrice entre mai 2025 et maintenant pour prioriser des refontes de titles ou de meta descriptions, relancez l'analyse après la correction des données.\u003C/p>\n\u003Ch3>Reporting client et KPIs\u003C/h3>\n\u003Cp>Pour les agences et les consultants, les impressions sont un KPI de reporting standard. Une hausse de 25% des impressions, même sans clics supplémentaires, peut être présentée comme un signal positif (\"la visibilité augmente, les clics suivront\"). Ce narratif est faux si les impressions sont gonflées.\u003C/p>\n\u003Cp>Recommandation pragmatique : ajoutez une note dans tous les rapports couvrant la période mai 2025 – correction confirmée, indiquant que les données d'impressions GSC sont suspectes et seront révisées.\u003C/p>\n\u003Ch3>Modèles de forecasting\u003C/h3>\n\u003Cp>Si votre modèle de prévision de trafic utilise les impressions GSC comme variable d'entrée — ce qui est courant dans les modèles de type \u003Ccode>clicks = impressions × CTR_modélisé\u003C/code> — vos projections pour 2026 sont biaisées. Le modèle a appris un niveau d'impressions artificiellement élevé.\u003C/p>\n\u003Cp>Après la correction, re-entraînez vos modèles sur les données corrigées. En attendant, basez vos forecasts sur les clics uniquement.\u003C/p>\n\u003Ch2>Automatiser la détection de ce type d'anomalie\u003C/h2>\n\u003Cp>Ce bug illustre un problème plus large : comment détecter une dérive dans des données tierces que vous ne contrôlez pas ?\u003C/p>\n\u003Ch3>Monitoring des ratios plutôt que des absolus\u003C/h3>\n\u003Cp>Les valeurs absolues d'impressions peuvent varier pour mille raisons légitimes (saisonnalité, nouveau contenu, core update). Ce qui trahit une anomalie de données, c'est la \u003Cstrong>rupture dans les ratios\u003C/strong>.\u003C/p>\n\u003Cp>Le ratio \u003Ccode>clics/impressions\u003C/code> (CTR) est normalement stable à position constante. Si la position moyenne ne bouge pas, que les clics sont stables, mais que le CTR chute — c'est un signal fort d'anomalie côté impressions.\u003C/p>\n\u003Cp>Voici un check automatisable en TypeScript qui interroge l'API GSC et alerte quand le CTR dévie de plus de 2 écarts-types par rapport à la moyenne glissante :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// ctr-anomaly-detector.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// À exécuter en cron quotidien via GitHub Actions ou un scheduler\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> { google } \u003C/span>\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'googleapis'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">interface\u003C/span>\u003Cspan style=\"color:#B392F0\"> DailyMetrics\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  date\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  impressions\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  clicks\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  ctr\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  position\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">async\u003C/span>\u003Cspan style=\"color:#F97583\"> function\u003C/span>\u003Cspan style=\"color:#B392F0\"> fetchMetrics\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  auth\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> any\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  siteUrl\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  startDate\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  endDate\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#B392F0\"> Promise\u003C/span>\u003Cspan style=\"color:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"color:#B392F0\">DailyMetrics\u003C/span>\u003Cspan style=\"color:#E1E4E8\">[]> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> searchconsole\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> google.\u003C/span>\u003Cspan style=\"color:#B392F0\">searchconsole\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({ version: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'v1'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, auth });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> response\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> searchconsole.searchanalytics.\u003C/span>\u003Cspan style=\"color:#B392F0\">query\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    siteUrl,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    requestBody: {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      startDate,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      endDate,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      dimensions: [\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'date'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      rowLimit: \u003C/span>\u003Cspan style=\"color:#79B8FF\">25000\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (response.data.rows \u003C/span>\u003Cspan style=\"color:#F97583\">||\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> []).\u003C/span>\u003Cspan style=\"color:#B392F0\">map\u003C/span>\u003Cspan style=\"color:#E1E4E8\">((\u003C/span>\u003Cspan style=\"color:#FFAB70\">row\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    date: row.keys\u003C/span>\u003Cspan style=\"color:#F97583\">!\u003C/span>\u003Cspan style=\"color:#E1E4E8\">[\u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    impressions: row.impressions\u003C/span>\u003Cspan style=\"color:#F97583\">!\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    clicks: row.clicks\u003C/span>\u003Cspan style=\"color:#F97583\">!\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    ctr: row.ctr\u003C/span>\u003Cspan style=\"color:#F97583\">!\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    position: row.position\u003C/span>\u003Cspan style=\"color:#F97583\">!\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  }));\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">function\u003C/span>\u003Cspan style=\"color:#B392F0\"> detectCtrAnomaly\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  metrics\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#B392F0\"> DailyMetrics\u003C/span>\u003Cspan style=\"color:#E1E4E8\">[],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  windowSize\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 30\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">  threshold\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 2.0\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"color:#FFAB70\">date\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#FFAB70\">ctr\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#FFAB70\">expected\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#FFAB70\">zScore\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> }[] {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> anomalies\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">    date\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> string\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#FFAB70\">ctr\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#FFAB70\">expected\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#FFAB70\">zScore\u003C/span>\u003Cspan style=\"color:#F97583\">:\u003C/span>\u003Cspan style=\"color:#79B8FF\"> number\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  }[] \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"color:#F97583\">let\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> i \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> windowSize; i \u003C/span>\u003Cspan style=\"color:#F97583\">&#x3C;\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> metrics.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; i\u003C/span>\u003Cspan style=\"color:#F97583\">++\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> window\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> metrics.\u003C/span>\u003Cspan style=\"color:#B392F0\">slice\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(i \u003C/span>\u003Cspan style=\"color:#F97583\">-\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> windowSize, i);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> mean\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> window.\u003C/span>\u003Cspan style=\"color:#B392F0\">reduce\u003C/span>\u003Cspan style=\"color:#E1E4E8\">((\u003C/span>\u003Cspan style=\"color:#FFAB70\">s\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">m\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> s \u003C/span>\u003Cspan style=\"color:#F97583\">+\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> m.ctr, \u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">/\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> windowSize;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> stdDev\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> Math.\u003C/span>\u003Cspan style=\"color:#B392F0\">sqrt\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      window.\u003C/span>\u003Cspan style=\"color:#B392F0\">reduce\u003C/span>\u003Cspan style=\"color:#E1E4E8\">((\u003C/span>\u003Cspan style=\"color:#FFAB70\">s\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">m\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> s \u003C/span>\u003Cspan style=\"color:#F97583\">+\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> Math.\u003C/span>\u003Cspan style=\"color:#B392F0\">pow\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(m.ctr \u003C/span>\u003Cspan style=\"color:#F97583\">-\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> mean, \u003C/span>\u003Cspan style=\"color:#79B8FF\">2\u003C/span>\u003Cspan style=\"color:#E1E4E8\">), \u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">/\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> windowSize\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    );\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (stdDev \u003C/span>\u003Cspan style=\"color:#F97583\">===\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">continue\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> current\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> metrics[i];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> zScore\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (current.ctr \u003C/span>\u003Cspan style=\"color:#F97583\">-\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> mean) \u003C/span>\u003Cspan style=\"color:#F97583\">/\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> stdDev;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    // On cherche spécifiquement une chute du CTR (z-score négatif)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    // combinée à une position stable\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> avgPosition\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      window.\u003C/span>\u003Cspan style=\"color:#B392F0\">reduce\u003C/span>\u003Cspan style=\"color:#E1E4E8\">((\u003C/span>\u003Cspan style=\"color:#FFAB70\">s\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">m\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> s \u003C/span>\u003Cspan style=\"color:#F97583\">+\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> m.position, \u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">/\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> windowSize;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> positionDrift\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> Math.\u003C/span>\u003Cspan style=\"color:#B392F0\">abs\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(current.position \u003C/span>\u003Cspan style=\"color:#F97583\">-\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> avgPosition);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (zScore \u003C/span>\u003Cspan style=\"color:#F97583\">&#x3C;\u003C/span>\u003Cspan style=\"color:#F97583\"> -\u003C/span>\u003Cspan style=\"color:#E1E4E8\">threshold \u003C/span>\u003Cspan style=\"color:#F97583\">&#x26;&#x26;\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> positionDrift \u003C/span>\u003Cspan style=\"color:#F97583\">&#x3C;\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1.0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      anomalies.\u003C/span>\u003Cspan style=\"color:#B392F0\">push\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        date: current.date,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        ctr: current.ctr,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        expected: mean,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        zScore,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> anomalies;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// Usage\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">async\u003C/span>\u003Cspan style=\"color:#F97583\"> function\u003C/span>\u003Cspan style=\"color:#B392F0\"> main\u003C/span>\u003Cspan style=\"color:#E1E4E8\">() {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> auth\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> new\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> google.auth.\u003C/span>\u003Cspan style=\"color:#B392F0\">GoogleAuth\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    keyFile: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'service-account-key.json'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    scopes: [\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'https://www.googleapis.com/auth/webmasters.readonly'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> metrics\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#B392F0\"> fetchMetrics\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> auth.\u003C/span>\u003Cspan style=\"color:#B392F0\">getClient\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    'sc-domain:votredomaine.fr'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    '2025-01-01'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    '2026-03-31'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  );\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> anomalies\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#B392F0\"> detectCtrAnomaly\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(metrics);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (anomalies.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#F97583\"> >\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">error\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`🔴 ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">anomalies\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} anomalies CTR détectées :`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    anomalies.\u003C/span>\u003Cspan style=\"color:#B392F0\">forEach\u003C/span>\u003Cspan style=\"color:#E1E4E8\">((\u003C/span>\u003Cspan style=\"color:#FFAB70\">a\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      console.\u003C/span>\u003Cspan style=\"color:#B392F0\">error\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        `  ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">a\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">date\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}: CTR=${\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#E1E4E8\">a\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">ctr\u003C/span>\u003Cspan style=\"color:#F97583\"> *\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 100\u003C/span>\u003Cspan style=\"color:#9ECBFF\">).\u003C/span>\u003Cspan style=\"color:#B392F0\">toFixed\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">2\u003C/span>\u003Cspan style=\"color:#9ECBFF\">)\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}% `\u003C/span>\u003Cspan style=\"color:#F97583\"> +\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        `(attendu: ${\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#E1E4E8\">a\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">expected\u003C/span>\u003Cspan style=\"color:#F97583\"> *\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 100\u003C/span>\u003Cspan style=\"color:#9ECBFF\">).\u003C/span>\u003Cspan style=\"color:#B392F0\">toFixed\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">2\u003C/span>\u003Cspan style=\"color:#9ECBFF\">)\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}%, z=${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">a\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">zScore\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#B392F0\">toFixed\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">2\u003C/span>\u003Cspan style=\"color:#9ECBFF\">)\u003C/span>\u003Cspan style=\"color:#9ECBFF\">})`\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      );\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    process.\u003C/span>\u003Cspan style=\"color:#B392F0\">exit\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">1\u003C/span>\u003Cspan style=\"color:#E1E4E8\">); \u003C/span>\u003Cspan style=\"color:#6A737D\">// Fail pour déclencher une alerte\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  } \u003C/span>\u003Cspan style=\"color:#F97583\">else\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'✅ Pas d\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\'\u003C/span>\u003Cspan style=\"color:#9ECBFF\">anomalie CTR détectée.'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">main\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Ce type de détection aurait signalé le problème dès la troisième semaine de mai 2025 — soit 10 mois avant la confirmation officielle de Google.\u003C/p>\n\u003Cp>Un outil comme Seogard, qui monitore les métriques SEO en continu et détecte les écarts statistiquement significatifs, permet d'automatiser ce type de surveillance sans maintenir des scripts maison. Le principe reste le même : surveiller les ratios, pas les absolus.\u003C/p>\n\u003Ch3>Intégration dans un pipeline CI/CD\u003C/h3>\n\u003Cp>Pour les équipes qui ont déjà des \u003Ca href=\"/blog/automatiser-les-checks-seo-dans-le-ci-cd\">checks SEO dans leur CI/CD\u003C/a>, ajoutez un job scheduled (pas déclenché au deploy, mais en cron) qui exécute ce type de vérification :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># .github/workflows/gsc-anomaly-check.yml\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">name\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">GSC Anomaly Detection\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">on\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">  schedule\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    - \u003C/span>\u003Cspan style=\"color:#85E89D\">cron\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'0 8 * * 1'\u003C/span>\u003Cspan style=\"color:#6A737D\">  # Tous les lundis à 8h UTC\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">  workflow_dispatch\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">jobs\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">  check-ctr-anomalies\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">    runs-on\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">ubuntu-latest\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">    steps\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      - \u003C/span>\u003Cspan style=\"color:#85E89D\">uses\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">actions/checkout@v4\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      - \u003C/span>\u003Cspan style=\"color:#85E89D\">uses\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">actions/setup-node@v4\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">        with\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">          node-version\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'20'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      - \u003C/span>\u003Cspan style=\"color:#85E89D\">run\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">npm ci\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      - \u003C/span>\u003Cspan style=\"color:#85E89D\">name\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">Run CTR anomaly detection\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">        env\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">          GOOGLE_SERVICE_ACCOUNT_KEY\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">${{ secrets.GSC_SERVICE_ACCOUNT_KEY }}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">        run\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">npx tsx scripts/ctr-anomaly-detector.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      - \u003C/span>\u003Cspan style=\"color:#85E89D\">name\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">Notify on failure\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">        if\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">failure()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">        uses\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">slackapi/slack-github-action@v1.27.0\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">        with\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">          payload\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#F97583\">|\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">            {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">              \"text\": \"⚠️ Anomalie CTR détectée dans Search Console. Vérifiez les données d'impressions.\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">            }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">        env\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#85E89D\">          SLACK_WEBHOOK_URL\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">${{ secrets.SLACK_WEBHOOK_URL }}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Ch2>Ce que ce bug révèle sur la fiabilité des données Google\u003C/h2>\n\u003Ch3>Search Console n'est pas une source de vérité absolue\u003C/h3>\n\u003Cp>Ce n'est pas la première anomalie de données dans Search Console. Google maintient une \u003Ca href=\"https://support.google.com/webmasters/answer/6211428\">liste des anomalies connues\u003C/a> qui documente des incidents récurrents — certains ayant duré plusieurs mois avant correction.\u003C/p>\n\u003Cp>Les données Search Console sont un \u003Cstrong>échantillon traité\u003C/strong> de ce que Google observe, pas un reflet exhaustif et exact de la réalité. Les impressions en particulier dépendent de définitions complexes (qu'est-ce qu'une \"impression\" pour un résultat en position 8 sur mobile ? pour un carrousel d'images ? pour un résultat Discover ?). La \u003Ca href=\"https://support.google.com/webmasters/answer/7042828\">documentation officielle Google sur les impressions\u003C/a> détaille ces nuances.\u003C/p>\n\u003Ch3>La redondance des sources de données n'est pas optionnelle\u003C/h3>\n\u003Cp>Si votre stack de reporting SEO repose à 100% sur l'API Search Console, vous avez un single point of failure sur la fiabilité des données. Croisez systématiquement avec :\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Logs serveur\u003C/strong> : le referer Google dans vos access logs donne le volume réel de visites organiques. Pas les impressions, mais les clics — ce qui permet de valider la cohérence.\u003C/li>\n\u003Cli>\u003Cstrong>Google Analytics / solution analytics côté client\u003C/strong> : pour une deuxième mesure du trafic entrant.\u003C/li>\n\u003Cli>\u003Cstrong>Outils de suivi de position tiers\u003C/strong> (Semrush, Ahrefs, etc.) : si votre position moyenne GSC reste stable mais que le CTR chute, c'est un signal d'anomalie côté impressions.\u003C/li>\n\u003C/ul>\n\u003Cp>Ce principe de triangulation est particulièrement critique quand vous interprétez les effets d'une \u003Ca href=\"/blog/google-begins-rolling-out-march-2026-core-update-via-sejournal-mattgsouthern\">core update\u003C/a> : il faut distinguer une variation réelle de performance d'un artefact de mesure.\u003C/p>\n\u003Ch3>L'impact sur les rapports GSC sous-exploités\u003C/h3>\n\u003Cp>Le rapport Discover, le rapport Google News, le rapport sur les performances vidéo — ces \u003Ca href=\"/blog/google-search-console-les-rapports-que-vous-ignorez\">rapports souvent ignorés\u003C/a> de Search Console utilisent le même pipeline de logging. Il est probable que le bug les ait également affectés, même si Google n'a pas explicitement confirmé le scope exact.\u003C/p>\n\u003Cp>Vérifiez vos données Discover avec la même méthodologie : si les impressions Discover ont bondi en mai 2025 sans que vos analytics ne confirment une hausse de trafic Discover, le bug est probablement en cause.\u003C/p>\n\u003Ch2>Actions concrètes : que faire maintenant\u003C/h2>\n\u003Ch3>Court terme (cette semaine)\u003C/h3>\n\u003Col>\n\u003Cli>\u003Cstrong>Identifiez l'amplitude du problème\u003C/strong> sur votre site avec le script Python ci-dessus ou via l'export manuel de GSC (Performance → Export → Google Sheets).\u003C/li>\n\u003Cli>\u003Cstrong>Documentez l'anomalie\u003C/strong> dans votre outil de reporting. Ajoutez une annotation sur la date du 13 mai 2025 dans Google Analytics et dans tout dashboard qui affiche des données GSC.\u003C/li>\n\u003Cli>\u003Cstrong>Gelez les décisions basées sur le CTR\u003C/strong> calculé à partir des données GSC sur la période affectée. Ne lancez pas de chantier d'optimisation de snippets basé sur un CTR artificiellement bas.\u003C/li>\n\u003C/ol>\n\u003Ch3>Moyen terme (après le correctif Google)\u003C/h3>\n\u003Col start=\"4\">\n\u003Cli>\u003Cstrong>Re-téléchargez les données corrigées\u003C/strong> via l'API une fois le rollout du fix confirmé. Comparez avec vos exports pré-correction pour quantifier l'écart exact sur votre site.\u003C/li>\n\u003Cli>\u003Cstrong>Recalibrez vos modèles de forecasting\u003C/strong> si vous utilisez les impressions comme variable d'entrée.\u003C/li>\n\u003Cli>\u003Cstrong>Auditez les décisions prises\u003C/strong> entre mai 2025 et la correction. Y a-t-il des chantiers lancés sur la base de données fausses ?\u003C/li>\n\u003C/ol>\n\u003Ch3>Long terme (process)\u003C/h3>\n\u003Col start=\"7\">\n\u003Cli>\u003Cstrong>Mettez en place un monitoring automatisé\u003C/strong> des ratios GSC (CTR à position constante, ratio impressions/clics). Le script TypeScript ci-dessus est un point de départ.\u003C/li>\n\u003Cli>\u003Cstrong>Diversifiez vos sources de données\u003C/strong> : ne faites jamais reposer un KPI stratégique sur une seule source, surtout une source que vous ne contrôlez pas.\u003C/li>\n\u003C/ol>\n\u003Cp>Ce bug est un rappel brutal : les données tierces peuvent mentir silencieusement pendant des mois. Un \u003Ca href=\"/blog/deploiement-vendredi-soir-comment-eviter-la-catastrophe-seo\">déploiement un vendredi soir\u003C/a> peut casser votre SEO et vous le saurez vite. Un bug de logging côté Google peut biaiser votre stratégie pendant un an — et vous ne le saurez que quand Google décidera de le communiquer.\u003C/p>\n\u003Cp>La seule défense durable, c'est la surveillance continue multi-sources, avec des alertes sur les écarts statistiques — pas sur les valeurs absolues. C'est exactement ce que des outils comme Seogard automatisent : détecter la dérive avant qu'elle ne devienne une erreur stratégique.\u003C/p>",null,12,[18,19,20,21,22],"google","search console","impressions","bug","données SEO","Bug Search Console : impressions gonflées depuis mai 2025","Fri Apr 03 2026 22:02:22 GMT+0000 (Coordinated Universal Time)",[26],{"_id":27,"slug":28,"__v":6,"author":7,"canonical":29,"category":10,"createdAt":30,"date":12,"description":31,"image":15,"imageAlt":15,"readingTime":32,"tags":33,"title":39,"updatedAt":40},"69cf3d0750ec067ac27cb851","why-your-content-doesn-t-appear-in-ai-overviews-even-if-it-ranks-in-the-top-10","https://seogard.io/blog/why-your-content-doesn-t-appear-in-ai-overviews-even-if-it-ranks-in-the-top-10","2026-04-03T04:07:35.039Z","Un top 10 Google ne garantit pas la citation en AI Overview. Optimisez structure, retrieval et signaux de confiance pour y apparaître.",14,[34,35,36,37,38],"AI Overviews","SEO technique","structured data","content optimization","Google SGE","Pourquoi votre contenu n'apparaît pas dans les AI Overviews","Fri Apr 03 2026 04:07:35 GMT+0000 (Coordinated Universal Time)"]