Structured data et visibilité locale : Google, AI Overviews, Local Pack

Un réseau de 47 cliniques dentaires perd 30 % de ses appels entrants en trois semaines. Pas de pénalité, pas de mise à jour d'algorithme. Le problème : une migration de CMS a supprimé le markup LocalBusiness de toutes les pages d'établissement. Google a commencé à reconcilier les données de Google Business Profile, du site web et de divers annuaires — avec des conflits sur les horaires et les numéros de téléphone. Les AI Overviews ont repris ces informations contradictoires et affiché des réponses fausses. Le structured data n'était pas un "nice to have" — c'était le point de vérité que Google utilisait pour arbitrer.

Le structured data comme couche de réconciliation dans l'écosystème Google

Google ne se contente plus d'afficher dix liens bleus. Pour une requête locale comme "dentiste urgence Lyon 3", le moteur doit alimenter simultanément le Local Pack (3-pack Maps), les résultats organiques, les AI Overviews, et potentiellement les réponses d'assistants vocaux. Chaque surface a ses propres besoins en données, mais toutes puisent dans le même Knowledge Graph.

Le problème fondamental : les sources de données business sont multiples et souvent contradictoires. Google Business Profile dit que le cabinet ferme à 19h. Le site web affiche 18h30. Un annuaire PagesJaunes mentionne 18h. Un avis Google parle d'une fermeture à 19h30 le vendredi.

Le structured data JSON-LD sur votre site web joue un rôle spécifique dans cette réconciliation. Google l'a documenté dans sa documentation sur les données structurées pour les entreprises locales : le markup LocalBusiness et ses sous-types permettent de déclarer explicitement et de manière machine-readable les attributs de votre établissement. Quand ce markup est cohérent avec votre fiche GBP, Google accorde une confiance élevée à ces données. Quand il est absent ou contradictoire, le moteur doit inférer — et il se trompe.

Ce qui change avec les AI Overviews

Les AI Overviews accentuent cette dynamique. Un résultat classique renvoyait vers votre page, où l'utilisateur pouvait vérifier l'information. Une AI Overview synthétise et présente l'information directement — si elle est fausse, l'utilisateur ne clique jamais pour vérifier. Les citations dans les AI Overviews provenant des pages les mieux classées ont chuté significativement, ce qui signifie que Google s'appuie davantage sur des sources structurées que sur le contenu textuel brut pour construire ces réponses.

Pour les requêtes locales, les AI Overviews font face à un défi supplémentaire : les données changent fréquemment (horaires spéciaux, fermetures temporaires, nouveaux services). Le structured data fournit un signal daté et explicite que les systèmes d'IA peuvent consommer sans ambiguïté.

Implémenter un markup LocalBusiness robuste : au-delà du minimum

La plupart des guides s'arrêtent à un JSON-LD basique avec nom, adresse, téléphone. C'est insuffisant pour servir de point de réconciliation fiable.

Voici un markup complet pour une page d'établissement d'un réseau de cliniques :

{
  "@context": "https://schema.org",
  "@type": "Dentist",
  "@id": "https://www.dentaplus.fr/cliniques/lyon-3-part-dieu#organization",
  "name": "DentaPlus Lyon Part-Dieu",
  "image": "https://www.dentaplus.fr/images/cliniques/lyon-3-facade.webp",
  "url": "https://www.dentaplus.fr/cliniques/lyon-3-part-dieu",
  "telephone": "+33-4-78-62-14-30",
  "email": "[email protected]",
  "priceRange": "€€",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "45 rue de la Part-Dieu",
    "addressLocality": "Lyon",
    "addressRegion": "Auvergne-Rhône-Alpes",
    "postalCode": "69003",
    "addressCountry": "FR"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 45.7604,
    "longitude": 4.8590
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday"],
      "opens": "08:30",
      "closes": "19:00"
    },
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": "Friday",
      "opens": "08:30",
      "closes": "17:00"
    },
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": "Saturday",
      "opens": "09:00",
      "closes": "13:00"
    }
  ],
  "specialOpeningHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "validFrom": "2026-03-15",
      "validThrough": "2026-03-15",
      "opens": "00:00",
      "closes": "00:00",
      "description": "Fermé - Formation annuelle"
    }
  ],
  "department": [
    {
      "@type": "MedicalBusiness",
      "name": "Orthodontie - DentaPlus Lyon Part-Dieu",
      "telephone": "+33-4-78-62-14-31",
      "medicalSpecialty": "Orthodontics"
    }
  ],
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.6",
    "reviewCount": "238"
  },
  "sameAs": [
    "https://www.google.com/maps/place/?q=place_id:ChIJ_____FAKE_ID",
    "https://www.facebook.com/DentaPlusLyon3",
    "https://www.doctolib.fr/dentiste/lyon/dentaplus-part-dieu"
  ],
  "hasMap": "https://www.google.com/maps/place/?q=place_id:ChIJ_____FAKE_ID",
  "isAccessibleForFree": false,
  "paymentAccepted": "Cash, Credit Card, Carte Vitale",
  "areaServed": {
    "@type": "GeoCircle",
    "geoMidpoint": {
      "@type": "GeoCoordinates",
      "latitude": 45.7604,
      "longitude": 4.8590
    },
    "geoRadius": "15000"
  }
}

Détails qui font la différence

L'@id explicite. En attribuant un identifiant URI stable à chaque entité, vous permettez à Google de relier sans ambiguïté cette entité à travers les différentes mentions sur votre site (page d'accueil, page contact, pages services). Sans @id, Google doit déduire que deux blocs JSON-LD sur deux pages différentes parlent du même établissement — et il peut échouer.

sameAs vers le Place ID Google Maps. C'est le pont explicite entre votre structured data et votre fiche GBP. Google peut l'utiliser pour valider la concordance des informations. Si vos horaires dans le JSON-LD correspondent à ceux de GBP, le signal de confiance est maximal.

specialOpeningHoursSpecification. Souvent omis, ce champ est critique pour les AI Overviews. Quand un utilisateur demande "clinique dentaire ouverte demain Lyon" un jour férié, Google a besoin de cette information structurée pour générer une réponse fiable.

areaServed. Ce champ aide les systèmes d'IA à déterminer la pertinence géographique sans dépendre uniquement de l'adresse physique. Un plombier situé dans le 3e arrondissement peut desservir tout Lyon — sans ce champ, les AI Overviews pourraient le limiter à son quartier.

Cohérence multi-source : le vrai levier de la visibilité locale

Le markup seul ne suffit pas. Sa puissance réside dans sa cohérence avec les autres sources de données. Les guidelines de Bing ont récemment ajouté des spécifications géographiques étendues, confirmant que les moteurs — pas seulement Google — investissent dans la réconciliation des signaux locaux.

Scénario concret : réseau de 47 établissements

Prenons un réseau de 47 cliniques dentaires réparties sur 12 villes françaises. Le site compte environ 850 pages : 47 pages d'établissements, 47 × 8 pages de services localisés, plus les pages institutionnelles.

Avant l'implémentation structurée :

  • 14 établissements avaient des divergences d'horaires entre le site et GBP
  • 8 avaient des numéros de téléphone différents (ancien numéro sur le site, nouveau sur GBP)
  • 23 n'avaient aucun structured data
  • Les AI Overviews généraient des réponses avec des horaires incorrects pour 6 cliniques
  • Trafic organique local (requêtes "[spécialité] + [ville]") : ~4 200 sessions/mois

Le plan d'action technique :

  1. Audit des divergences — Export GBP via l'API Google My Business, scraping des données affichées sur le site, comparaison automatisée.

  2. Centralisation des données — Création d'un fichier de référence unique (source of truth) alimentant à la fois le CMS (et donc le JSON-LD) et l'API GBP.

  3. Déploiement du markup — Injection dynamique du JSON-LD depuis les données centralisées.

  4. Validation continue — Monitoring des divergences entre markup déployé et fiche GBP.

Résultats après 8 semaines :

  • Zéro divergence détectée entre site et GBP
  • Apparition dans le Local Pack pour 31 cliniques (contre 19 avant)
  • Trafic organique local : ~6 100 sessions/mois (+45 %)
  • Réduction de 80 % des signalements d'horaires incorrects via Google Maps

Le point clé : le gain n'est pas venu du markup lui-même, mais de la résolution des conflits que le markup a rendu possible. Google disposait enfin d'un signal cohérent et machine-readable pour chaque établissement.

Automatiser la génération et la validation du structured data

Sur un réseau multi-établissements, la génération manuelle du JSON-LD est un vecteur d'erreur majeur. Voici une approche avec un script Node.js qui génère le markup à partir d'une source de données centralisée et le valide :

import { readFileSync, writeFileSync } from 'fs';

interface Establishment {
  slug: string;
  name: string;
  type: string;
  phone: string;
  email: string;
  street: string;
  city: string;
  region: string;
  zip: string;
  lat: number;
  lng: number;
  hours: { days: string[]; opens: string; closes: string }[];
  specialHours: { date: string; opens: string; closes: string; note: string }[];
  ratingValue: number;
  reviewCount: number;
  gbpPlaceId: string;
  serviceRadius: number;
}

function generateLocalBusinessSchema(est: Establishment): object {
  const baseUrl = 'https://www.dentaplus.fr/cliniques';

  return {
    '@context': 'https://schema.org',
    '@type': est.type,
    '@id': `${baseUrl}/${est.slug}#organization`,
    name: est.name,
    url: `${baseUrl}/${est.slug}`,
    telephone: est.phone,
    email: est.email,
    address: {
      '@type': 'PostalAddress',
      streetAddress: est.street,
      addressLocality: est.city,
      addressRegion: est.region,
      postalCode: est.zip,
      addressCountry: 'FR',
    },
    geo: {
      '@type': 'GeoCoordinates',
      latitude: est.lat,
      longitude: est.lng,
    },
    openingHoursSpecification: est.hours.map((h) => ({
      '@type': 'OpeningHoursSpecification',
      dayOfWeek: h.days,
      opens: h.opens,
      closes: h.closes,
    })),
    specialOpeningHoursSpecification: est.specialHours.map((sh) => ({
      '@type': 'OpeningHoursSpecification',
      validFrom: sh.date,
      validThrough: sh.date,
      opens: sh.opens,
      closes: sh.closes,
      description: sh.note,
    })),
    aggregateRating: {
      '@type': 'AggregateRating',
      ratingValue: est.ratingValue.toString(),
      reviewCount: est.reviewCount.toString(),
    },
    sameAs: [
      `https://www.google.com/maps/place/?q=place_id:${est.gbpPlaceId}`,
    ],
    areaServed: {
      '@type': 'GeoCircle',
      geoMidpoint: {
        '@type': 'GeoCoordinates',
        latitude: est.lat,
        longitude: est.lng,
      },
      geoRadius: est.serviceRadius.toString(),
    },
  };
}

// Validation basique de cohérence
function validateSchema(schema: Record<string, any>): string[] {
  const errors: string[] = [];

  if (!schema.telephone?.match(/^\+33-\d-\d{2}-\d{2}-\d{2}-\d{2}$/)) {
    errors.push(`Format téléphone invalide: ${schema.telephone}`);
  }

  if (!schema.geo?.latitude || !schema.geo?.longitude) {
    errors.push('Coordonnées GPS manquantes');
  }

  const hours = schema.openingHoursSpecification || [];
  for (const h of hours) {
    if (h.opens >= h.closes) {
      errors.push(`Horaires incohérents: ${h.opens} >= ${h.closes} pour ${h.dayOfWeek}`);
    }
  }

  if (!schema['@id']) {
    errors.push('@id manquant — réconciliation inter-pages impossible');
  }

  return errors;
}

// Exécution
const establishments: Establishment[] = JSON.parse(
  readFileSync('./data/establishments.json', 'utf-8')
);

let totalErrors = 0;

for (const est of establishments) {
  const schema = generateLocalBusinessSchema(est);
  const errors = validateSchema(schema as Record<string, any>);

  if (errors.length > 0) {
    console.error(`❌ ${est.name}:`, errors);
    totalErrors += errors.length;
  } else {
    writeFileSync(
      `./output/${est.slug}.json`,
      JSON.stringify(schema, null, 2)
    );
  }
}

console.log(`\nGénération terminée. ${totalErrors} erreurs détectées.`);

Ce script fait trois choses que la génération manuelle ne fait pas : il applique un format téléphonique uniforme, il vérifie la cohérence logique des horaires, et il garantit la présence de l'@id sur chaque entité. Sur 47 établissements, ce type de validation automatisée détecte typiquement 5 à 10 erreurs que personne n'aurait repérées à l'œil.

Validation avec les outils Google

Après déploiement, la validation ne s'arrête pas au Rich Results Test. Utilisez l'URL Inspection API de Search Console pour vérifier que Googlebot parse effectivement votre JSON-LD. Un piège classique : le markup est présent dans le HTML initial mais écrasé par un framework JavaScript côté client.

Pour les sites en SSR, vérifiez dans Chrome DevTools > Sources que le JSON-LD est dans le HTML servi au premier render, pas injecté après hydration. La distinction est critique : si le structured data est injecté après le DOMContentLoaded, Googlebot en mode WRS (Web Rendering Service) peut le voir, mais les crawlers d'IA tiers (Bing Chat, Perplexity) qui ne font pas de rendering JavaScript ne le verront pas. Ce sujet rejoint les problématiques de JavaScript SEO que Google a récemment mis à jour.

Structured data et agents IA : préparer l'après-Search

L'optimisation pour les agents IA assistants (Gemini, ChatGPT avec browsing, Perplexity) est une extension naturelle du structured data local. Ces agents ne disposent pas tous de la même capacité de rendering que Googlebot. Leur capacité à extraire des informations business fiables dépend largement de la présence de données structurées dans le HTML brut.

L'optimisation pour les agents IA assistants repose sur un principe simple : plus vos données sont explicites et machine-readable, moins l'agent doit inférer. Et moins il infère, moins il hallucine.

Le rôle de sameAs et du Knowledge Graph

La propriété sameAs dans votre markup local ne sert pas uniquement à Google. Elle établit un réseau d'identité entre les différentes représentations de votre entité à travers le web. Quand un agent IA croise votre fiche GBP, votre page Doctolib et votre site web, les liens sameAs lui permettent de confirmer qu'il s'agit du même établissement.

C'est une forme de stratégie d'optimisation centrée sur le contexte : vous ne cherchez pas à ranker un mot-clé, vous construisez une entité cohérente que les systèmes d'IA peuvent consommer avec confiance.

Edge case : données contradictoires entre markup et GBP

Que se passe-t-il quand votre JSON-LD dit "ouvert samedi matin" mais votre GBP dit "fermé samedi" ? Google donne la priorité à GBP pour les fonctionnalités Maps (Local Pack, Google Maps). Mais pour les résultats organiques et les AI Overviews, le markup du site web a un poids significatif. Le résultat : l'AI Overview pourrait afficher "ouvert samedi" tandis que le Local Pack affiche "fermé samedi" — pour la même requête.

Ce type de conflit est invisible sans monitoring automatisé. Un outil de monitoring technique comme SEOGard peut détecter les divergences entre le markup déployé sur vos pages et signaler les régressions (markup supprimé lors d'un déploiement, horaires modifiés sur le site mais pas sur GBP).

Surveiller les régressions de structured data à l'échelle

Le structured data local est fragile. Les causes de régression les plus fréquentes :

  • Redéploiement CMS : un template mis à jour qui oublie l'inclusion du JSON-LD
  • Modification de données source : un changement d'horaires dans le backoffice qui n'est pas propagé au JSON-LD
  • Migration technique : passage de SSR à CSR (ou inversement) qui modifie le moment d'injection du markup
  • CDN/cache : une page cachée qui sert un ancien markup pendant des jours

Configurer un check automatisé avec Screaming Frog

Screaming Frog permet d'extraire et valider le structured data à l'échelle. Voici une configuration pour auditer un réseau multi-établissements :

Configuration > Spider > Extraction
──────────────────────────────────────
1. Aller dans Configuration > Custom > Extraction

2. Ajouter les extracteurs suivants :

   Extracteur 1 - JSON-LD LocalBusiness presence
   Type: CSSPath
   Selector: script[type="application/ld+json"]
   Extract: Inner HTML

   Extracteur 2 - Telephone in markup
   Type: Regex
   Regex: "telephone"\s*:\s*"([^"]+)"
   
   Extracteur 3 - Opening Hours presence
   Type: Regex
   Regex: "openingHoursSpecification"

3. Configuration > Spider > Advanced
   - Cocher "Enable JavaScript Rendering"
   - Window Size: 1920x1080
   - Timeout: 10s (suffisant pour le SSR, augmenter à 20s si CSR)

4. Filtrer les URLs à crawler :
   Configuration > Include
   Pattern: /cliniques/
   
5. Exporter les résultats et comparer avec le fichier source de vérité

Exportez les résultats en CSV et comparez automatiquement les numéros de téléphone extraits avec votre fichier establishments.json. Sur un réseau de 47 établissements, ce workflow prend 15 minutes et détecte immédiatement les pages où le markup a été supprimé ou modifié de manière non intentionnelle.

Pour les sites plus volumineux (500+ pages d'établissements), planifiez ce crawl en CI/CD. L'API Screaming Frog permet d'intégrer cette vérification dans votre pipeline de déploiement.

Le piège du cache et de la fraîcheur

Un point souvent négligé : la fréquence de recrawl de Googlebot sur vos pages d'établissement. Si vous modifiez les horaires dans votre CMS et que le JSON-LD est mis à jour, mais que Googlebot ne recrawle la page que dans 3 semaines, les AI Overviews continueront d'afficher les anciens horaires.

Pour accélérer le recrawl après une modification, utilisez l'API Indexing de Google (limitée aux LocalBusiness et JobPosting) ou soumettez les URLs modifiées via Search Console. Ce sujet est lié à la gestion du sitemap XML : un sitemap avec des <lastmod> précis aide Googlebot à prioriser le recrawl des pages récemment modifiées.

Attention au crawl budget : sur un réseau de centaines d'établissements, soumettre toutes les URLs simultanément après un changement global (modification des horaires pour un jour férié national, par exemple) peut saturer votre quota de recrawl. Échelonnez les soumissions.

Au-delà de LocalBusiness : les types de markup complémentaires

Le markup LocalBusiness seul ne couvre pas tous les signaux utiles pour la visibilité locale. Plusieurs types complémentaires renforcent la compréhension de Google et des IA :

FAQ structurée par établissement

Chaque page d'établissement peut inclure un markup FAQPage avec des questions localisées. Cela alimente directement les AI Overviews pour des requêtes conversationnelles. La clé : les questions doivent être authentiquement locales, pas des questions génériques dupliquées sur chaque page.

Event pour les actions temporaires

Si vos établissements organisent des événements (journées portes ouvertes, campagnes de dépistage), le markup Event avec location pointant vers votre @id d'établissement crée un lien explicite entre l'événement et le lieu.

Service pour les prestations spécifiques

Le type Service avec areaServed et provider pointant vers votre @id d'établissement est particulièrement utile pour les requêtes de type "implant dentaire Lyon 3". Les AI Overviews peuvent alors associer un service spécifique à un établissement spécifique dans une zone géographique.

Gérer le contenu dupliqué des pages locales

Un réseau multi-établissements génère naturellement du contenu similaire entre les pages locales. Le structured data ne résout pas ce problème — il peut même l'aggraver si le même texte est markup de la même façon sur 47 pages. Assurez-vous que chaque page d'établissement a un canonical correctement configuré et que le contenu textuel est suffisamment différencié. L'enjeu d'index bloat est réel si chaque combinaison service × établissement génère une page avec peu de contenu unique.

Les limites du structured data pour la visibilité locale

Il serait malhonnête de présenter le structured data comme une solution magique. Plusieurs limites méritent d'être posées clairement.

Le structured data ne remplace pas les signaux de pertinence classiques. Un markup LocalBusiness parfait sur une page avec un contenu pauvre et zéro backlink ne vous fera pas apparaître dans le Local Pack. Les facteurs de ranking local restent : proximité, pertinence, prominence (avis, citations, backlinks locaux).

Google peut ignorer votre markup. Si le contenu visible de la page contredit le structured data, Google peut choisir de ne pas l'utiliser. Un markup qui déclare "ratingValue": "4.8" alors que la page n'affiche aucun avis visible est un signal de spam.

Les types Schema ne sont pas tous supportés. Le fait que areaServed existe dans la spécification Schema.org ne garantit pas que Google l'utilise pour le ranking. Consultez toujours la documentation officielle de Google sur les données structurées pour vérifier quels types et propriétés sont effectivement supportés et génèrent des résultats enrichis.

La réconciliation IA reste opaque. Comment exactement Gemini ou les AI Overviews pondèrent le structured data par rapport à d'autres sources — c'est une boîte noire. L'approche pragmatique : maximiser la cohérence entre toutes vos sources de données et considérer le markup comme un vote explicite en faveur de vos propres données.

Stratégie de déploiement : par où commencer

Si vous gérez un réseau multi-établissements et que votre structured data local est inexistant ou fragmentaire, voici l'ordre de priorité :

  1. Auditer l'existant — Crawlez toutes vos pages d'établissement avec Screaming Frog + extraction custom. Identifiez les pages sans markup, avec un markup partiel, ou avec des données divergentes de GBP.

  2. Centraliser la source de vérité — Un fichier JSON ou une API interne qui contient les données de référence pour chaque établissement. C'est ce fichier qui alimente le CMS, le JSON-LD, et les mises à jour GBP.

  3. Déployer le markup complet — En SSR, dans le HTML initial. Validez avec le Rich Results Test et l'URL Inspection de Search Console.

  4. Automatiser la détection de régressions — Intégrez la validation du markup dans votre CI/CD ou utilisez un outil de monitoring continu comme SEOGard pour détecter les suppressions ou modifications non intentionnelles du structured data.

  5. Itérer sur les types complémentaires — Une fois le LocalBusiness en place et cohérent, ajoutez FAQPage, Service, et Event progressivement.

Le structured data local n'est plus un bonus pour les rich snippets — c'est l'infrastructure de confiance sur laquelle Google et les systèmes d'IA s'appuient pour éviter de diffuser des informations erronées sur vos établissements. Le maintien de la fraîcheur du contenu dans un contexte d'IA passe aussi par ces signaux structurés. La question n'est pas "est-ce que ça vaut le coup" — c'est "combien de trafic local perdez-vous en laissant Google deviner".

Articles connexes

Actualités SEO28 mars 2026

Core Update Mars 2026 : analyse technique et plan d'action

Google déploie la March 2026 Core Update. Analyse technique, scénarios d'impact concrets et méthodologie de diagnostic pour les équipes SEO.

Actualités SEO27 mars 2026

Page Speed : transformer un site lent en machine de course

Guide technique avancé pour optimiser la vitesse de chargement : poids, puissance serveur, navigation du critical path. Code, configs et scénarios réels.

Actualités SEO26 mars 2026

Écrire pour l'IA search : playbook technique du contenu machine-readable

Structurez votre contenu pour que les LLMs l'extraient et le citent. Code, schémas, configs et scénarios concrets pour l'AI search.