API Platform génère nativement du JSON-LD conforme à Schema.org. Exploitez cette capacité pour exposer votre catalogue e-commerce d'une façon que Google peut lire, comprendre et convertir en rich snippets directement dans les SERP.
Pourquoi Google a besoin d'une API structurée pour votre catalogue
Googlebot crawle votre HTML, mais il interprète le sens de votre contenu. Pour un catalogue de 10 000 produits, deux problèmes se posent : la profondeur de crawl (le bot n'atteint pas tous vos produits) et l'ambiguïté sémantique (il ne sait pas distinguer le prix du poids sans aide).
Les données structurées Schema.org résolvent le second problème. Injectées en JSON-LD dans vos pages ou exposées via une API indexable, elles permettent à Google de :
- Afficher des rich snippets (prix, stock, avis, promotions) directement dans les résultats
- Alimenter Google Shopping sans flux Merchant Center manuel
- Comprendre la relation entre variantes, catégories et marques
- Indexer plus vite via l'Indexing API couplée à votre flux
API Platform construit ce pont automatiquement. Son format de réponse par défaut est JSON-LD, avec un contexte @context qui mappe vos propriétés sur le vocabulaire Schema.org.
Configurer l'ApiResource Product avec Schema.org
La première étape consiste à déclarer votre entité Product comme ressource API Platform en spécifiant les types Schema.org correspondants.
// src/Entity/Product.php
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
#[ApiResource(
types: ['https://schema.org/Product'],
operations: [
new Get(),
new GetCollection(),
],
normalizationContext: ['groups' => ['product:read']],
paginationItemsPerPage: 100,
)]
class Product
{
#[ApiProperty(types: ['https://schema.org/name'])]
#[Groups(['product:read'])]
public string $name = '';
#[ApiProperty(types: ['https://schema.org/description'])]
#[Groups(['product:read'])]
public ?string $description = null;
#[ApiProperty(types: ['https://schema.org/sku'])]
#[Groups(['product:read'])]
public string $sku = '';
#[ApiProperty(types: ['https://schema.org/image'])]
#[Groups(['product:read'])]
public ?string $imageUrl = null;
#[ApiProperty(types: ['https://schema.org/offers'])]
#[Groups(['product:read'])]
public ?Offer $offer = null;
}
L'entité Offer est une entité embarquée qui porte le prix et la disponibilité. API Platform la sérialisera comme un objet Schema.org Offer imbriqué dans la réponse.
// src/Entity/Offer.php
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\ApiProperty;
#[ApiResource(types: ['https://schema.org/Offer'])]
class Offer
{
#[ApiProperty(types: ['https://schema.org/price'])]
#[Groups(['product:read'])]
public float $price = 0.0;
#[ApiProperty(types: ['https://schema.org/priceCurrency'])]
#[Groups(['product:read'])]
public string $priceCurrency = 'EUR';
#[ApiProperty(types: ['https://schema.org/availability'])]
#[Groups(['product:read'])]
public string $availability = 'https://schema.org/InStock';
#[ApiProperty(types: ['https://schema.org/url'])]
#[Groups(['product:read'])]
public ?string $url = null;
}
La réponse JSON-LD générée automatiquement
Sans une ligne de code supplémentaire, une requête GET /api/products/42 avec le header Accept: application/ld+json retourne :
{
"@context": "/api/contexts/Product",
"@id": "/api/products/42",
"@type": "https://schema.org/Product",
"name": "Chaussures Running Pro X",
"description": "Chaussure légère pour trail, semelle Vibram...",
"sku": "RUN-PRX-44",
"imageUrl": "https://example.com/images/run-pro-x.webp",
"offer": {
"@type": "https://schema.org/Offer",
"price": 129.99,
"priceCurrency": "EUR",
"availability": "https://schema.org/InStock",
"url": "https://example.com/chaussures-running-pro-x"
}
}
Google comprend nativement ce format. Le @type: "https://schema.org/Product" déclenche le parsing comme données structurées, exactement comme un bloc <script type="application/ld+json"> dans votre HTML.
Injecter les structured data dans vos pages HTML
Pour les pages produit rendues côté serveur (ou via SSR en headless), récupérez les données depuis votre API interne et injectez-les dans un bloc JSON-LD dans le <head>.
// Dans votre contrôleur Twig ou votre getStaticProps Next.js
$product = $this->productApiClient->find($id);
// Injecter dans le template
$structuredData = json_encode([
'@context' => 'https://schema.org',
'@type' => 'Product',
'name' => $product->name,
'sku' => $product->sku,
'description' => $product->description,
'image' => $product->imageUrl,
'offers' => [
'@type' => 'Offer',
'price' => $product->offer->price,
'priceCurrency' => $product->offer->priceCurrency,
'availability' => $product->offer->availability,
'url' => $product->offer->url,
'priceValidUntil' => date('Y-m-d', strtotime('+30 days')),
],
]);
{# templates/product/show.html.twig #}
<head>
<script type="application/ld+json">
{{ structuredData|raw }}
</script>
</head>
Point critique : le champ priceValidUntil est fortement recommandé par Google pour afficher les rich snippets prix. Sans lui, Google peut refuser d'afficher le prix dans les SERP.
Exposer un flux de catalogue pour Google Shopping
Google Merchant Center peut consommer un flux de produits directement depuis une URL. Créez un endpoint dédié qui retourne votre catalogue complet en JSON structuré, paginé via le mécanisme natif d'API Platform.
// Flux accessible à l'adresse /api/products?page=1&itemsPerPage=1000
// Avec Accept: application/ld+json
// La réponse inclut la pagination hydra:
{
"@context": "/api/contexts/Product",
"@id": "/api/products",
"@type": "hydra:Collection",
"hydra:totalItems": 8432,
"hydra:member": [...],
"hydra:view": {
"@id": "/api/products?page=1",
"hydra:next": "/api/products?page=2"
}
}
Ajoutez un filtre sur la disponibilité pour n'exposer que les produits en stock dans votre flux Merchant :
#[ApiResource(
operations: [
new GetCollection(
uriTemplate: '/products/merchant-feed',
filters: ['product.availability_filter'],
),
]
)]
Brancher le sitemap sur votre API pour accélérer l'indexation
Couplé au sitemap dynamique (voir l'article sur les sitemaps Symfony), votre API produit devient la source de vérité pour l'indexation. Récupérez les URLs depuis l'API au lieu de taper directement en base :
// src/Service/SitemapGenerator.php
public function generateProductUrls(): iterable
{
$page = 1;
do {
$response = $this->httpClient->request('GET', '/api/products', [
'query' => ['page' => $page, 'itemsPerPage' => 500],
'headers' => ['Accept' => 'application/ld+json'],
]);
$data = $response->toArray();
foreach ($data['hydra:member'] as $product) {
yield new SitemapUrl(
loc: $product['offer']['url'],
changefreq: 'weekly',
priority: 0.8,
lastmod: $product['updatedAt'] ?? null,
);
}
$page++;
} while (isset($data['hydra:view']['hydra:next']));
}
Sécuriser et optimiser l'API pour le crawl
Une API publique exposée pour Google doit être optimisée pour les accès en lecture massive :
- Cache HTTP : ajoutez des headers
Cache-Control: public, max-age=3600sur les endpoints de catalogue. Nginx ou Varnish les mettra en cache sans toucher PHP. - Authentification sélective : les endpoints lecture du catalogue sont publics, les endpoints d'écriture sont protégés par JWT ou token API.
- Rate limiting : protégez contre le scraping abusif avec symfony/rate-limiter sur les endpoints publics.
- Sérialisation partielle : n'exposez que les groupes de sérialisation nécessaires à Google (
product:read), pas les données internes.
// config/packages/api_platform.yaml
api_platform:
defaults:
cache_headers:
max_age: 3600
shared_max_age: 3600
vary: ['Accept', 'Accept-Language']
Mesurer l'impact dans Google Search Console
Une fois les données structurées en place, surveillez deux sections dans la Search Console :
- Améliorations > Produits : nombre de pages avec rich snippets valides vs. erreurs. Une erreur fréquente est
price missingouavailability missing. - Performances > Type de recherche : Shopping : impressions et clics générés par les rich snippets produit.
Comptez 2 à 4 semaines avant de voir les premiers rich snippets apparaître, le temps que Google recrawle vos pages et valide vos données structurées.
Conclusion
API Platform transforme Symfony en générateur automatique de données structurées Schema.org. En une dizaine d'annotations, votre catalogue devient lisible par Google, éligible aux rich snippets et compatible avec Google Shopping — sans duplication de code ni maintenance d'un flux XML manuel.
La combinaison API Platform + JSON-LD + sitemap dynamique est le socle technique d'un catalogue e-commerce réellement bien indexé. Vous voulez auditer votre implémentation ou démarrer l'intégration sur votre projet PrestaShop/Symfony ? Contactez-moi pour en discuter.