Publier des publications sur Google et Facebook
Publiez annonces, événements et offres, programmez-les pour plusieurs établissements, vérifiez leur mise en ligne, gérez-les et traitez les webhooks.
Une publication est une fiche datée sur le profil Google Business Profile ou la page Facebook d'un établissement : une annonce, un événement ou une offre. Un premier appel la crée, la publication s'effectue en arrière-plan et un second appel vous indique si chaque site l'a acceptée.
Les trois types de publication utilisent POST /posts avec le corps enveloppé dans input. Un corps à plat est rejeté avec un 400. Les SDK construisent l'enveloppe et valident les données avant l'envoi.
1. Publier une annonce
Une annonce comprend un message, un bouton facultatif et une image facultative.
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "Grand Opening Announcement", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="], "postType": "ANNOUNCEMENT", "postSites": ["GOOGLE"], "postMessage": [ { "site": "GOOGLE", "message": "We are now open! Visit us this week for our grand opening." } ], "postCta": [ { "site": "GOOGLE", "type": "LEARN_MORE", "url": "https://acmekitchen.example.com/opening" } ], "postMediaUrl": [ { "site": "GOOGLE", "url": "https://cdn.example.com/opening.jpg", "type": "IMAGE" } ] }}'{ "data": { "createSocialPost": { "success": true, "errors": [], "socialPost": { "id": "U29jaWFsUG9zdDo0NDEyMg==", "name": "Grand Opening Announcement", "status": "INPROGRESS", "type": "ANNOUNCEMENT", "sites": ["GOOGLE"], "publishDetails": [ { "site": "GOOGLE", "status": "PUBLISHING", "createdDate": "2026-06-28", "submissionError": null } ], "postLinks": [], "locationIds": [1800289] } } }}Conservez socialPost.id. status a pour valeur initiale INPROGRESS et postLinks reste vide jusqu'à ce qu'un site accepte la publication ; l'étape 5 permet de relire ces deux champs.
2. Publier un événement
Un événement ajoute postContextInfo avec un title, un startDay, un endDay et des horaires d'affichage facultatifs. Google rejette un événement sans titre, et les SDK lèvent ValidationError avant l'envoi si le titre est absent.
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "Summer Sale Kickoff Event", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="], "postType": "EVENT", "postSites": ["GOOGLE"], "postContextInfo": { "title": "Summer Sale Kickoff", "startDay": "2026-07-15", "startTime": "10:00am", "endDay": "2026-07-15", "endTime": "06:00pm" }, "postMessage": [ { "site": "GOOGLE", "message": "Join us for the Summer Sale kickoff: refreshments, giveaways, and up to 40% off in store." } ] }}'La réponse a la même structure qu'à l'étape 1, avec type: "EVENT" et les dates renvoyées sous socialPost.contextInfo. postContextInfo contient les dates propres à l'événement ; postScheduledDates (étape 4) détermine quand la publication elle-même est mise en ligne et retirée.
3. Publier une offre
Le champ postContextInfo d'une offre contient un title, un couponCode, un discount, un redeemUrl, un termsConditions et la période de validité définie par startDay et endDay. Associez l'offre à un bouton GET_OFFER sur Facebook ou ORDER sur Google ; il n'existe pas de type REDEEM.
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "Summer 25% Off", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="], "postType": "OFFER", "postSites": ["GOOGLE"], "postContextInfo": { "title": "25% off storewide", "couponCode": "SUMMER25", "discount": "25%", "redeemUrl": "https://acmekitchen.example.com/summer", "termsConditions": "Valid in store only through July 6. Cannot be combined with other offers.", "startDay": "2026-06-22", "endDay": "2026-07-06" }, "postMessage": [ { "site": "GOOGLE", "message": "Celebrate with us: 25% off everything in store this week." } ] }}'La réponse renvoie les champs de l'offre sous socialPost.contextInfo et définit scheduledStartDate et scheduledEndDate à partir de la période de validité.
4. Envoyer une publication à plusieurs établissements selon un calendrier
POST /bulk-posts accepte le même input et envoie une campagne à chaque id dans locationIds. Ajoutez postScheduledDates avec startDatetime et endDatetime au format ISO-8601 pour publier ultérieurement et retirer automatiquement la publication ; celle-ci est alors créée avec le statut SCHEDULED. Attribuez à chaque site son propre message lorsque le ton diffère.
curl -X POST https://listingsapi.com/api/v4/bulk-posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "July Reopening Announcement", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="], "postType": "ANNOUNCEMENT", "postSites": ["GOOGLE", "FACEBOOK"], "postMessage": [ { "site": "GOOGLE", "message": "We are back! Visit us this week for our summer menu." }, { "site": "FACEBOOK", "message": "We are back! Swing by for the new summer menu." } ], "postScheduledDates": { "startDatetime": "2026-07-10T09:00:00Z", "endDatetime": "2026-07-24T23:59:00Z" } }}'Le point de terminaison d'envoi groupé renvoie la même structure que POST /posts ; la réponse utilise donc elle aussi la clé createSocialPost, avec status: "SCHEDULED" et les deux identifiants dans locationIds. Un problème de validation entraîne le renvoi de 200 avec success: false et un tableau errors renseigné ; les SDK lèvent alors ValidationError. Relisez une campagne d'envoi groupé avec GET /bulk-posts/{postId}, qui ajoute un statut par établissement.
5. Vérifier le statut de publication et récupérer les liens des publications
GET /posts/{postId} renvoie la campagne sous socialPostView. Interrogez ce point de terminaison après une création jusqu'à ce que socialPostInfo.status quitte l'état INPROGRESS, puis lisez publishDetails[] pour connaître le résultat sur chaque site et postLinks[] pour obtenir les URL des publications en ligne. Espacez les interrogations de plusieurs dizaines de secondes ; un 429 est accompagné de Retry-After.
curl https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "socialPostView": { "socialPostInfo": { "id": "U29jaWFsUG9zdDo0NDEyMg==", "status": "SUCCESS", "views": 128, "clicks": 14, "publishDetails": [ { "site": "GOOGLE", "status": "PUBLISHED", "publishedDate": "2026-06-28", "submissionError": null } ], "postLinks": [ { "site": "GOOGLE", "link": "https://www.google.com/maps/place/?post=44122" } ] }, "socialPostAnalyticsBySite": [ { "site": "GOOGLE", "views": 128, "clicks": 14, "reactions": 3, "shares": 0, "comments": 1 } ] } }}views, clicks, reactions, shares et comments s'accumulent dans le même enregistrement, avec une ventilation par site dans socialPostAnalyticsBySite. Les SDK renvoient directement socialPostInfo, ce qui explique pourquoi les exemples lisent post.status.
6. Répertorier les publications d'un établissement et en supprimer une
GET /locations/{locationId}/posts est une page paginée par décalage (page, perPage jusqu'à 50) contenant toutes les campagnes qui incluent l'établissement, sous postsByLocation avec records[] et pageInfo.totalRecords. Transmettez l'identifiant base64 sans le modifier ; ne l'encodez pas pour une URL. Il n'y a pas de filtre par type : sélectionnez donc les offres ou les événements à partir de type dans votre propre code. Les campagnes couvrant plusieurs établissements de l'étape 4 sont répertoriées séparément par GET /locations/{locationId}/bulk-posts.
DELETE /posts/{postId} supprime la campagne ici et la retire de tous les sites sur lesquels elle a été diffusée. Cette action est irréversible : vérifiez donc d'abord l'identifiant dans la liste.
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/posts?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY" curl -X DELETE https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"Une suppression renvoie data.deleteSocialPost avec success: true et le socialPostId supprimé. La suppression d'un identifiant déjà supprimé renvoie à la place une réponse errors avec data: null, ce qui permet de détecter facilement une seconde suppression.
7. Réagir aux webhooks des publications
Lorsqu'une URL de webhook est définie dans /dashboard/webhooks, quatre événements couvrent le cycle de vie d'une publication, tous dans la charge utile standard. Ils sont déclenchés par établissement, et non par campagne : une publication en masse destinée à 20 établissements produit 20 valeurs post_item_id avec un seul post_id commun.
function handle(payload: { event: string; location_id: string; data: Record<string, unknown> }) {if (!payload.event.startsWith('local_post.')) return; const key = String(payload.data.post_item_id); // one row per locationswitch (payload.event) { case 'local_post.published': markLive(key, payload.location_id, payload.data.post_external_id); break; case 'local_post.rejected': markRejected(key, payload.location_id, payload.data.rejection_reason); break; case 'local_post.deleted': markDeleted(key); break;}}published_url est généralement absent : continuez donc à interroger l'API comme à l'étape 5 pour obtenir postLinks si vous avez besoin d'un lien cliquable. Renvoyez 200 dans les 5 secondes ; aucune nouvelle tentative n'est effectuée. Le récepteur et la vérification de la signature sont décrits dans Réagir aux événements avec des webhooks.
Étapes suivantes
- Connecter Google et importer des établissements et Connecter des pages Facebook : le prérequis pour chaque site.
- Récupérer les statistiques des profils : les vues et les actions au niveau du profil, en complément des compteurs par publication présentés ici.
- Réagir aux événements avec des webhooks : le récepteur auquel ces événements sont transmis.
- Référence : Créer une publication d'annonce, Créer une publication d'événement, Créer une publication d'offre, Créer une publication en masse, Obtenir une publication, Récupérer les publications d'un établissement, Supprimer une publication.