register_rest_route(). Cette fonction, disponible nativement depuis WordPress 4.4, suffit à construire un point de terminaison sur mesure sans dépendre d’une extension tierce, dès lors que le besoin sort du cadre standard de l’API REST fournie par défaut. C’est exactement le cas rencontré ici : un partenaire agrégateur immobilier demandait un flux structuré au format Schema.org RealEstateListing, un format que l’API REST native de WordPress, pensée pour exposer des articles bruts, ne produit pas telle quelle.
Ce billet ne traite pas la synchronisation bidirectionnelle avec le partenaire, uniquement l’exposition en lecture des annonces via ce point de terminaison, consommé côté agrégateur à intervalle régulier.
Pourquoi un endpoint dédié plutôt qu’un export générique
L’API REST native expose les annonces, enregistrées comme type de contenu personnalisé annonce_immo, sous une forme générique : titre, contenu, champs personnalisés bruts sans structure Schema.org. Transformer cette réponse générique au format attendu par le partenaire côté agrégateur aurait ajouté une couche de traitement chez un tiers qui ne maîtrise pas la structure interne du site. Il était plus robuste de produire directement, côté WordPress, une réponse déjà conforme au vocabulaire RealEstateListing.
Un endpoint dédié permet également de ne exposer que les champs nécessaires au partenaire, sans risque de fuite d’informations internes présentes dans les métadonnées brutes de l’annonce.
Le snippet : déclaration du point de terminaison

add_action( 'rest_api_init', function () {
register_rest_route( 'partenaire/v1', '/annonces', array(
'methods' => 'GET',
'callback' => 'partenaire_get_annonces',
'permission_callback' => 'partenaire_verifier_cle_api',
'args' => array(
'page' => array(
'default' => 1,
'sanitize_callback' => 'absint',
),
),
) );
} );
La fonction partenaire_verifier_cle_api() contrôle une clé transmise en en-tête HTTP avant d’autoriser l’accès, une précaution nécessaire puisque ce flux, bien que public dans son principe, ne doit être consommé que par le partenaire identifié.
Construire la réponse au format RealEstateListing
function partenaire_get_annonces( WP_REST_Request $request ) {
$page = $request->get_param( 'page' );
$query = new WP_Query( array(
'post_type' => 'annonce_immo',
'post_status' => 'publish',
'posts_per_page' => 50,
'paged' => $page,
) );
$annonces = array();
foreach ( $query->posts as $post ) {
$annonces[] = array(
'@context' => 'https://schema.org',
'@type' => 'RealEstateListing',
'name' => get_the_title( $post ),
'url' => get_permalink( $post ),
'price' => get_post_meta( $post->ID, 'prix', true ),
'floorSize' => get_post_meta( $post->ID, 'surface_m2', true ),
'numberOfRooms' => get_post_meta( $post->ID, 'nb_pieces', true ),
'address' => get_post_meta( $post->ID, 'adresse', true ),
);
}
return rest_ensure_response( array(
'page' => (int) $page,
'totalPages'=> $query->max_num_pages,
'annonces' => $annonces,
) );
}
Chaque annonce est ainsi renvoyée avec la structure exacte attendue par le partenaire, en réutilisant le vocabulaire Schema.org déjà employé par ailleurs pour le balisage des pages d’annonces individuelles, ce qui garantit une cohérence entre ce que voit un moteur de recherche sur la page HTML et ce que consomme l’agrégateur via ce flux.
Pagination et cache : deux points souvent négligés
La pagination via paged et max_num_pages évite de renvoyer l’intégralité du catalogue en une seule réponse, un point critique dès que le nombre d’annonces dépasse quelques centaines. Côté cache, la réponse de cet endpoint est mise en cache via un transitoire WordPress d’une durée de quinze minutes, un compromis jugé raisonnable entre fraîcheur des données pour le partenaire et charge serveur, la fréquence de mise à jour des annonces ne justifiant pas un flux calculé à chaque requête.
- Un transitoire par numéro de page, invalidé automatiquement à sa durée d’expiration.
- Une invalidation manuelle possible via un hook déclenché à la publication d’une nouvelle annonce, pour les cas où la fraîcheur immédiate compte davantage.
En résumé
Construire un point de terminaison REST sur mesure avec register_rest_route() reste la solution la plus robuste dès qu’un partenaire exige un format structuré précis que l’API native ne produit pas nativement. Le travail principal ne réside pas dans la déclaration de la route elle-même, mais dans la construction fidèle du vocabulaire Schema.org attendu et dans une gestion de pagination et de cache adaptée à la volumétrie réelle du catalogue.