Post

Ogni endpoint post: esempi cURL, Python e Node, annunci, eventi, offerte, pianificazione, campagne per sede e massive, polling dello stato ed eliminazione.

Un post è un breve aggiornamento pubblicato sul Google Business Profile o sulla pagina Facebook di una sede: un annuncio, un evento con date definite o un'offerta con un coupon. Una campagna può rivolgersi a molte sedi e a entrambi i siti contemporaneamente. La pubblicazione è asincrona, quindi ogni chiamata di creazione restituisce subito una risposta e devi effettuare il polling per ottenere il risultato. I tre endpoint GET funzionano con una chiave Read (lettura); per creare ed eliminare serve una chiave Write (scrittura).

Panoramica degli endpoint

MetodoPercorsoFunzioneRiferimento
POST/postsPubblica un annuncio, un semplice messaggio con pulsante e immagine facoltativi.Crea un post di annuncio
POST/postsPubblica un evento, con un titolo e un intervallo temporale di inizio e fine.Crea un post di evento
POST/postsPubblica un'offerta, con un codice coupon, uno sconto e le condizioni.Crea un post di offerta
GET/posts/{postId}Una campagna con lo stato di pubblicazione e i conteggi delle interazioni per ciascun sito.Ottieni un post
GET/locations/{locationId}/postsUna pagina di campagne rivolte a una sede.Recupera i post per una sede
DELETE/posts/{postId}Elimina definitivamente una campagna e rimuovila da tutti i siti su cui è pubblicata.Elimina un post
POST/bulk-postsUna campagna distribuita su molte sedi.Crea un post in blocco
GET/bulk-posts/{postId}Una campagna in blocco con dati analitici suddivisi per sede.Recupera un post in blocco tramite ID
GET/locations/{locationId}/bulk-postsCampagne in blocco che includono una sede, con filtri e ordinamento.Recupera i post in blocco per una sede

Valori che userai ovunque

Tutte e quattro le enumerazioni seguenti sono condivise da tutti gli endpoint di creazione.

CampoValori accettati
postTypeANNOUNCEMENT, EVENT, OFFER, COVID19, PRODUCT
postSites e ogni siteGOOGLE, FACEBOOK
postCta[].typeBOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, GET_OFFER
postMediaUrl[].typeIMAGE, VIDEO

Pubblica un annuncio

POST /posts

Un annuncio accetta un messaggio e, facoltativamente, un pulsante e un'immagine per sito. Fornisci una voce postMessage per ogni sito elencato in postSites. La risposta restituisce la nuova campagna con status impostato su INPROGRESS, perché la pubblicazione avviene in background.

Parametri

NomeInObbligatorioNote
input.postNamebodySìNome interno della campagna, mai mostrato ai clienti.
input.locationIdsbodySìArray di id di sede in base64.
input.postTypebodySìANNOUNCEMENT in questo caso.
input.postSitesbodySìArray di GOOGLE e FACEBOOK.
input.postMessagebodyNoArray di oggetti con site e message.
input.postCtabodyNoArray di oggetti con site, type, url.
input.postMediaUrlbodyNoArray di oggetti con site, url, type.
input.clientMutationIdbodyNoRestituito senza modifiche per la correlazione.
Announcement
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://jennyhome.example/opening" }    ],    "postMediaUrl": [      { "site": "GOOGLE", "url": "https://cdn.example.com/opening.jpg", "type": "IMAGE" }    ]  }}'
JSON
{  "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. È l'id del post in base64 richiesto da tutti gli altri endpoint descritti qui. Entrambe le funzioni di supporto degli SDK accettano una singola stringa message e la espandono in una voce per sito, quindi devi costruire manualmente l'array postMessage solo in cURL.

Pubblica un evento

POST /posts

Un evento viene visualizzato come un riquadro con date. La pianificazione dell'evento stesso si trova in postContextInfo. L'oggetto separato postScheduledDates controlla quando il post stesso appare e scade, un intervallo che di solito corrisponde al periodo che precede l'evento. Google rifiuta un evento senza titolo ed entrambi gli SDK generano un errore di validazione prima dell'invio se manca title.

Parametri

NomeInObbligatorioNote
input.postTypebodySìEVENT in questo caso.
input.postContextInfo.titlebodySì, in praticaTitolo dell'evento. Google rifiuta gli eventi che ne sono privi.
input.postContextInfo.startDay, endDaybodyNoDate, YYYY-MM-DD.
input.postContextInfo.startTime, endTimebodyNoStringhe da visualizzare come 10:00am.
input.postScheduledDatesbodyNoOggetto con startDatetime, endDatetime, removalSites.

Si applica anche tutto quanto riportato nella tabella dell'annuncio.

Event
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 and up to 40 percent off in store." }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyMw==",        "status": "INPROGRESS",        "type": "EVENT",        "scheduledStartDate": "2026-07-15",        "scheduledEndDate": "2026-07-15",        "contextInfo": {          "title": "Summer Sale Kickoff",          "startDay": "2026-07-15",          "startTime": "10:00am",          "endDay": "2026-07-15",          "endTime": "06:00pm"        }      }    }  }}

Il valore contextInfo restituito è l'intervallo temporale dell'evento. scheduledStartDate e scheduledEndDate descrivono la pianificazione del post stesso, che in questo caso coincide con le date dell'evento perché non è stato inviato alcun postScheduledDates.

Pubblica un'offerta

POST /posts

Un'offerta è una scheda promozionale che contiene un codice coupon, uno sconto, un link per usufruirne e le condizioni. Tutto questo va in postContextInfo, mentre startDay e endDay delimitano la validità dell'offerta anziché quella del post.

Parametri

NomeInObbligatorioNote
input.postTypebodySìQui, OFFER.
input.postContextInfo.titlebodySì, in praticaAssegna un'etichetta all'offerta. Entrambi gli SDK lo richiedono.
input.postContextInfo.couponCodebodyNoCodice che il cliente utilizza per usufruire dell'offerta.
input.postContextInfo.discountbodyNoTesto libero, ad esempio 25%.
input.postContextInfo.redeemUrlbodyNoDove è possibile usufruire dell'offerta.
input.postContextInfo.termsConditionsbodyNoClausole e condizioni.
input.postContextInfo.startDay, endDaybodyNoIntervallo di validità, YYYY-MM-DD.
Offer
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "Festive 25 Percent Off",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="],    "postType": "OFFER",    "postSites": ["GOOGLE"],    "postContextInfo": {      "title": "25% off storewide",      "couponCode": "FESTIVE25",      "discount": "25%",      "redeemUrl": "https://jennyhome.example/festive",      "termsConditions": "Valid in-store only through July 6.",      "startDay": "2026-06-22",      "endDay": "2026-07-06"    },    "postMessage": [      { "site": "GOOGLE", "message": "Celebrate with us, 25 percent off everything in store this week." }    ],    "postCta": [      { "site": "GOOGLE", "type": "GET_OFFER", "url": "https://jennyhome.example/festive" }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyNA==",        "status": "INPROGRESS",        "type": "OFFER",        "contextInfo": {          "title": "25% off storewide",          "couponCode": "FESTIVE25",          "discount": "25%",          "startDay": "2026-06-22",          "endDay": "2026-07-06"        }      }    }  }}

Google accetta ORDER e Facebook accetta GET_OFFER per i pulsanti delle offerte. Se una promozione termina in anticipo, elimina la campagna anziché modificarla.

Leggi un post e il suo stato

GET /posts/{postId}

Restituisce il risultato della pubblicazione di una campagna accettata. La risposta contiene, sotto data.socialPostView, il contenuto della campagna, una voce publishDetails per ogni sito, i contatori delle interazioni e i link ai post online una volta completata la pubblicazione.

Parametri

NomeInObbligatorioNote
postIdpathSìID del post in Base64 ottenuto dalla risposta alla creazione o da un elenco.
Get a post
curl https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "socialPostView": {      "socialPostId": "U29jaWFsUG9zdDo0NDEyMg==",      "socialPostInfo": {        "status": "SUCCESS",        "type": "ANNOUNCEMENT",        "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" }        ]      },      "publishedLocationsCount": 1,      "socialPostAnalyticsBySite": [        { "site": "GOOGLE", "views": 128, "clicks": 14, "reactions": 3, "shares": 0, "comments": 1 }      ]    }  }}

Due campi di stato sono rilevanti e rispondono a domande diverse.

CampoValoreSignificato
socialPostInfo.statusINPROGRESSAccettato, la pubblicazione è ancora in corso.
SUCCESSPubblicazione completata. Leggi publishDetails per il risultato relativo a ciascun sito.
SCHEDULEDIn attesa dell'apertura della finestra postScheduledDates.
EXPIREDLa finestra programmata si è chiusa e il post non è più online.
publishDetails[].statusPUBLISHINGQuel sito non ha ancora risposto.
PUBLISHEDOnline su quel sito. Cerca l'URL in postLinks.
ERRORQuel sito lo ha rifiutato. submissionError ne indica il motivo.

Una campagna può riportare SUCCESS mentre uno dei siti al suo interno presenta ERROR, quindi controlla sempre publishDetails anziché soltanto lo stato di primo livello.

Elenca i post di una sede

GET /locations/{locationId}/posts

Una cronologia con paginazione basata su offset di tutte le campagne che includono una sede, sotto data.postsByLocation. Usala per verificare cosa è già online prima di programmare qualcosa di nuovo.

Parametri

NomeInObbligatorioNote
locationIdpathSìID in Base64, passato senza modifiche.
pagequeryNoNumero di pagina a partire da 1, valore predefinito 1.
perPagequeryNoValore predefinito 10, non deve superare 50.
Posts on a location
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/posts?page=1&perPage=10" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "postsByLocation": {      "records": [        {          "id": "U29jaWFsUG9zdDo0NDEyMg==",          "name": "Summer Hours Update",          "status": "SUCCESS",          "type": "ANNOUNCEMENT",          "createdAt": "2026-06-28",          "views": 128,          "clicks": 14,          "sites": ["GOOGLE"]        }      ],      "pageInfo": { "totalPages": 1, "hasNextPage": false, "totalRecords": 1 }    }  }}

Questa route non dispone di filtri lato server. Accetta solo page e perPage e ignora qualsiasi altro parametro, quindi leggi type e status in ogni record e applica i filtri nel tuo codice. Per ordinare e filtrare lato server, usa invece l'endpoint per l'elenco in blocco riportato di seguito.

Elimina un post

DELETE /posts/{postId}

L'eliminazione è permanente e rimuove il post da ogni sito su cui è stato pubblicato. Non è possibile annullarla e non esiste un endpoint di modifica, quindi per correggere un post online devi eliminarlo e pubblicarne uno sostitutivo.

Parametri

NomeInObbligatorioNote
postIdpathSìID del post in Base64.
clientMutationIdbodyNoFacoltativo, restituito senza modifiche per la correlazione.
Delete
curl -X DELETE https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "clientMutationId": "delete-44122-req-1" }'
JSON
{  "data": {    "deleteSocialPost": {      "success": true,      "socialPostId": "U29jaWFsUG9zdDo0NDEyMg=="    }  }}

L'eliminazione di un post sconosciuto o già eliminato restituisce una risposta di errore con data impostato su null anziché su success: false.

Pubblica una campagna su più sedi

POST /bulk-posts

Lo stesso contratto di POST /posts, pensato per gruppi di sedi: elenca tutte le sedi in locationIds e una sola campagna le coprirà tutte. La risposta usa la chiave createSocialPost, come POST /posts.

Parametri

NomeInObbligatorioNote
input.postNamebodySìNome interno della campagna.
input.locationIdsbodySìTutti gli ID in base64 delle sedi su cui pubblicare.
input.postTypebodySìEVENT, OFFER e PRODUCT richiedono anche postContextInfo o products.
input.postSitesbodySìGOOGLE, FACEBOOK o entrambi.
input.postScheduledDatesbodyNostartDatetime e endDatetime in formato ISO 8601. Crea il post come SCHEDULED.
input.componentIdentifierbodyNoUn tuo riferimento, utile in seguito come filtro.
Bulk publish
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"    }  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "errors": null,      "socialPost": {        "id": "U29jaWFsUG9zdDo5OTIwMQ==",        "name": "July Reopening Announcement",        "status": "SCHEDULED",        "type": "ANNOUNCEMENT",        "scheduledStartDate": "2026-07-10",        "scheduledEndDate": "2026-07-24",        "sites": ["GOOGLE", "FACEBOOK"],        "locationIds": [1800289, 1800290]      }    }  }}

Le funzioni di supporto di entrambi gli SDK impostano Google e Facebook come valore predefinito di sites, espandono per te una mappa dei messaggi per sito e codificano gli ID numerici delle sedi, quindi una pubblicazione in blocco è di solito la chiamata più breve in entrambi gli SDK.

Leggere una singola campagna in blocco

GET /bulk-posts/{postId}

La controparte per le operazioni in blocco di GET /posts/{postId}. Restituisce la stessa struttura, con publishedLocationsCount maggiore di uno e un dettaglio delle analisi per punto vendita in socialPostAnalyticsByLocationId.

Parametri

NomeInObbligatorioNote
postIdpathSìID del post in blocco in Base64, non un intero non codificato.
Get a bulk post
curl https://listingsapi.com/api/v4/bulk-posts/U29jaWFsUG9zdDo1MTA4OA== \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "socialPostView": {      "socialPostId": "U29jaWFsUG9zdDo1MTA4OA==",      "socialPostInfo": {        "name": "Festive Offer - All Stores",        "status": "SUCCESS",        "type": "OFFER",        "views": 4210,        "clicks": 318,        "sites": ["GOOGLE", "FACEBOOK"]      },      "socialPostAnalyticsByLocationId": [        {          "locationId": 1800289,          "siteStats": [            { "site": "GOOGLE", "status": "PUBLISHED", "views": 1040, "clicks": 80 },            { "site": "FACEBOOK", "status": "PUBLISHED", "views": 360, "clicks": 26 }          ]        }      ]    }  }}

socialPostAnalyticsBySite riporta i totali della campagna per tutti i punti vendita, mentre socialPostAnalyticsByLocationId suddivide gli stessi numeri per punto vendita. Esamina i valori di status per punto vendita per trovare le sedi in cui si è verificato un errore su un sito.

Elencare le campagne in blocco di una sede

GET /locations/{locationId}/bulk-posts

Paginazione tramite offset sotto data.rollupSocialPosts. A differenza dell'elenco per sede, questa route accetta filtri e ordinamento, il che la rende l'endpoint adatto per una dashboard delle campagne.

Parametri

NomeInObbligatorioNote
locationIdpathSìID in Base64, passato senza modifiche.
page, perPagequeryNoPaginazione tramite offset.
filtersqueryNoCodificato in JSON, per type, status, creationDateRange o siteUrls.
sortFieldsqueryNoCodificato in JSON, ad esempio il campo created_at con ordine Descending.
locationIdsqueryNoArray codificato in JSON degli ID delle sedi aggiuntive da includere.
componentIdentifierqueryNoLimita i risultati alle campagne create dalla tua integrazione.

I campi utilizzabili per l'ordinamento sono name, type, status, created_at, schedule_start_date, schedule_end_date, client_name, clicks, views e shares.

Bulk posts on a location
curl -G https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/bulk-posts \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'page=1' \--data-urlencode 'perPage=10' \--data-urlencode 'sortFields={"field":"created_at","order":"Descending"}'
JSON
{  "data": {    "rollupSocialPosts": {      "records": [        {          "id": "U29jaWFsUG9zdDo1MTA4OA==",          "name": "Festive Offer - All Stores",          "status": "SUCCESS",          "type": "OFFER",          "createdAt": "2026-06-20",          "views": 4210,          "clicks": 318        }      ],      "pageInfo": { "totalPages": 1, "hasNextPage": false, "totalRecords": 1 }    }  }}

Entrambi gli SDK codificano automaticamente in JSON gli oggetti filters e sortFields, quindi passali come dizionari o oggetti nativi.

Errori che incontrerai

Codice o statoSignificatoSoluzione
400: variabile $input mancanteIl corpo non è stato racchiuso in input.Annida tutti i campi sotto un'unica chiave input.
SY90016 come stato HTTP 400È stata usata una chiave Read per un'operazione di creazione o eliminazione.Usa una chiave Write.
401Chiave mancante o non valida.Invia Authorization: API <YOUR_API_KEY>.
429 con Retry-AfterTroppe chiamate, spesso dovute al polling dello stato di un post.Attendi il numero di secondi indicato. Vedi Limiti di frequenza.
200 con success: falseValidazione non riuscita.Leggi errors in data.createSocialPost.
per_page must not exceed 50perPage supera il limite massimo nell'elenco delle sedi.Richiedi 50 o meno.
tag is not a supported parameter for this querytag inviato alla route dei post per sede.Invia solo page e perPage.

Elenco completo in Codici di errore.

Passaggi successivi