Pubblica post su Google e Facebook
Pubblica annunci, eventi e offerte, programma un post per più sedi, verificane l'uscita su ogni sito, gestisci i post per sede e reagisci ai webhook.
Un post è una scheda con una data sul Google Business Profile o sulla Pagina Facebook di una sede: un annuncio, un evento o un'offerta. Una chiamata lo crea, la pubblicazione avviene in background e una seconda chiamata ti indica se ciascun sito lo ha accettato.
Tutti e tre i tipi di post usano POST /posts con il corpo racchiuso in input. Un corpo privo di questa struttura viene rifiutato con 400. Gli SDK costruiscono la struttura contenitore e convalidano il payload prima dell'invio.
1. Pubblica un annuncio
Un annuncio prevede un messaggio, un pulsante facoltativo e un'immagine facoltativa.
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] } } }}Conserva socialPost.id. status ha inizialmente il valore INPROGRESS e postLinks è vuoto finché un sito non accetta il post; al passaggio 5 vengono riletti entrambi.
2. Pubblica un evento
Un evento aggiunge postContextInfo con title, startDay, endDay e orari di visualizzazione facoltativi. Google rifiuta un evento senza titolo e, se il titolo manca, gli SDK generano ValidationError prima dell'invio.
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 risposta ha la stessa struttura del passaggio 1, con type: "EVENT" e le date restituite sotto socialPost.contextInfo. postContextInfo contiene le date dell'evento stesso; postScheduledDates (passaggio 4) controlla quando il post viene pubblicato e ritirato.
3. Pubblica un'offerta
Il campo postContextInfo di un'offerta contiene title, couponCode, discount, redeemUrl, termsConditions e il periodo di validità definito da startDay e endDay. Abbina l'offerta a un pulsante GET_OFFER su Facebook o ORDER su Google; non esiste un tipo 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 risposta restituisce i campi dell'offerta sotto socialPost.contextInfo e imposta scheduledStartDate e scheduledEndDate in base al periodo di validità.
4. Invia un post a più sedi, con una pubblicazione programmata
POST /bulk-posts accetta lo stesso input e invia una campagna a ogni id in locationIds. Aggiungi postScheduledDates con startDatetime e endDatetime in formato ISO-8601 per pubblicare in un secondo momento e ritirare automaticamente il post; il post viene quindi creato come SCHEDULED. Assegna a ciascun sito un messaggio specifico quando il tono cambia.
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" } }}'L'endpoint per le operazioni in blocco restituisce la stessa struttura di POST /posts, quindi anche la risposta usa la chiave createSocialPost, con status: "SCHEDULED" ed entrambi gli id in locationIds. Un problema di convalida restituisce 200 con success: false e un array errors popolato; in questo caso gli SDK generano ValidationError. Rileggi una campagna in blocco con GET /bulk-posts/{postId}, che aggiunge lo stato per ciascuna sede.
5. Controlla lo stato di pubblicazione e raccogli i link dei post
GET /posts/{postId} restituisce la campagna sotto socialPostView. Interrogalo periodicamente dopo la creazione finché socialPostInfo.status non passa da INPROGRESS a un altro valore, quindi leggi publishDetails[] per l'esito su ciascun sito e postLinks[] per gli URL dei post pubblicati. Distanzia le interrogazioni di decine di secondi; 429 è accompagnato da 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 e comments si accumulano nello stesso record, suddivisi per sito in socialPostAnalyticsBySite. Gli SDK restituiscono direttamente socialPostInfo, motivo per cui gli esempi leggono post.status.
6. Elenca i post di una sede ed eliminane uno
GET /locations/{locationId}/posts è una pagina basata su offset (page, perPage fino a 50) di tutte le campagne che includono la sede, sotto postsByLocation con records[] e pageInfo.totalRecords. Passa l'id base64 senza modificarlo; non applicare la codifica URL. Non è disponibile alcun filtro per tipo, quindi seleziona le offerte o gli eventi da type nel tuo codice. Le campagne per più sedi del passaggio 4 vengono elencate separatamente da GET /locations/{locationId}/bulk-posts.
DELETE /posts/{postId} rimuove la campagna qui e da tutti i siti su cui è stata pubblicata. L'operazione non può essere annullata, quindi verifica prima l'id nell'elenco.
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"Una richiesta di eliminazione restituisce data.deleteSocialPost con success: true e il socialPostId rimosso. L'eliminazione di un id già rimosso restituisce invece una risposta errors con data: null, quindi è facile rilevare una seconda richiesta di eliminazione.
7. Reagisci ai webhook dei post
Con un URL webhook impostato in /dashboard/webhooks, quattro eventi coprono il ciclo di vita del post, tutti nel payload standard. Si attivano per sede, non per campagna: un post inviato in blocco a 20 sedi produce 20 valori di post_item_id con un unico post_id condiviso.
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 è solitamente assente, quindi continua a eseguire il polling del passaggio 5 per ottenere postLinks se ti serve un link cliccabile. Rispondi con 200 entro 5 secondi; non sono previsti nuovi tentativi. Il ricevitore e la verifica della firma sono descritti in Reagisci agli eventi con i webhook.
Passaggi successivi
- Collega Google e importa le sedi e Collega le Pagine Facebook: il prerequisito per ciascun sito.
- Recupera le analisi del profilo: visualizzazioni e azioni a livello di profilo, insieme ai contatori per singolo post descritti qui.
- Reagisci agli eventi con i webhook: il ricevitore a cui arrivano questi eventi.
- Riferimenti: Crea un post di annuncio, Crea un post di evento, Crea un post di offerta, Crea un post in blocco, Ottieni un post, Recupera i post di una sede, Elimina un post.