Sedi
Endpoint sedi con esempi cURL, Python e Node in ordine d’uso: elenco, ricerca, recupero per id/codice negozio, creazione, modifica, archiviazione e foto.
Schede, recensioni, post e dati analitici sono tutti associati all'ID di una sede. La risorsa Locations ha 13 endpoint: 4 di lettura per trovare le sedi, 4 di scrittura per crearle, modificarle e archiviarle, e 5 per gestire le foto. Una chiave Read (lettura) consente le operazioni di lettura. Ogni POST richiede una chiave Write (scrittura); l'uso di una chiave Read per un'operazione di scrittura restituisce HTTP 400 con SY90016.
Ogni id in questa guida è un id Relay in base64, ad esempio TG9jYXRpb246MTgwMDI4OQ== (che, decodificato, corrisponde a Location:1800289). Entrambi gli SDK accettano anche il valore numerico databaseId e lo codificano per te.
Panoramica degli endpoint
Elenca tutte le sedi
GET /locations scorre tutte le sedi dell'account pagina per pagina, a partire dalle più recenti. È una connessione basata su cursori: edges[].node contiene ciascun record e pageInfo.endCursor fornisce il valore del parametro after per la pagina successiva. Usalo per sincronizzare l'intero account con il tuo sistema.
Parametri
curl "https://listingsapi.com/api/v4/locations?first=50" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "allLocations": { "edges": [ { "cursor": "TG9jYXRpb246MTgwMDI4OQ==", "node": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "storeId": null, "approved": "APPROVED", "archived": false, "archivalScheduledAt": null, "city": "Austin", "stateIso": "TX", "countryIso": "US", "subCategoryId": 1432, "planName": "Launch", "placeActionLinks": [], "discoveredPlaceActionLinks": [] } } ], "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "endCursor": "TG9jYXRpb246MTgwMDI4OQ==", "total": 1 } } }}Leggi pageInfo.hasNextPage e fermati quando il suo valore è false. Il conteggio a livello di pagina è pageInfo.total. Il valore id del nodo è l'identificatore richiesto da tutti gli altri endpoint.
Cerca sedi
GET /locations/search cerca una parola chiave nel nome della sede, nell'indirizzo stradale, nella città e nell'id del punto vendita. È una connessione basata su cursori con la stessa struttura di edges[].node e pageInfo.endCursor dell'elenco. Usalo per un selettore con suggerimenti durante la digitazione o per verificare che un punto vendita esista prima di crearlo.
Parametri
Entrambi gli SDK accettano anche un elenco fields per limitare la ricerca a campi specifici, ad esempio solo name.
curl "https://listingsapi.com/api/v4/locations/search?query=Jenny&first=10" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "searchLocations": { "edges": [ { "cursor": "TG9jYXRpb246MTgwMDI4OQ==", "node": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "storeId": null, "street": "600 Congress Ave, Suite 200", "city": "Austin", "approved": "APPROVED", "archived": false } } ], "pageInfo": { "endCursor": "TG9jYXRpb246MTgwMDI4OQ==", "hasNextPage": false, "hasPreviousPage": false, "total": 1 } } }}Un array edges vuoto indica che non è stata trovata alcuna corrispondenza. Il nodo contiene l'indirizzo completo, la categoria e lo stato approved, quindi una riga dei risultati non richiede una seconda ricerca.
Recupera le sedi per ID
GET /locations-by-ids restituisce i record completi per un gruppo di ID base64 con una sola chiamata, usando meno richieste rispetto a una chiamata per sede. Vengono recuperate sia le sedi attive sia quelle archiviate. Restituisce un semplice array, non una connessione. Usalo per ottenere i record completi a partire dagli ID presenti nel payload di un webhook o nel tuo archivio.
Parametri
curl -G https://listingsapi.com/api/v4/locations-by-ids \--data-urlencode 'ids=["TG9jYXRpb246MTgwMDI4OQ==","TG9jYXRpb246MTgwMDI5MA=="]' \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationsByIds": [ { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "storeId": null, "name": "Jenny Home", "archived": false, "archivedAt": null, "city": "Austin", "stateIso": "TX", "countryIso": "US", "subCategoryId": 1432, "businessHours": [], "locationPhotos": [], "placeActionLinks": [], "discoveredPlaceActionLinks": [] } ] }}Gli ID che non esistono nel tuo account vengono ignorati senza generare un errore. Confronta il numero di record restituiti con il numero di ID inviati per individuare gli ID obsoleti.
Recupera le sedi per codice del punto vendita
GET /locations-by-store-codes esegue la ricerca inversa: recupera i record completi a partire dai tuoi codici dei punti vendita. Restituisce un semplice array. Usalo quando il tuo POS o il tuo elenco di sedi in franchising identifica le sedi tramite il codice del punto vendita e non hai mai memorizzato i nostri ID.
Parametri
curl -G https://listingsapi.com/api/v4/locations-by-store-codes \--data-urlencode 'storeCodes=["ACME01","ACME02"]' \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationsByStoreCodes": [ { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "storeId": "ACME01", "name": "Jenny Home", "archived": false, "city": "Austin", "countryIso": "US", "planName": "Launch" } ] }}Associa il valore storeId di ciascun oggetto alla voce corrispondente nel tuo elenco. I codici senza corrispondenza sono assenti. Una sede ha un codice del punto vendita solo se storeId è stato impostato durante la creazione o l'aggiornamento.
Crea una sede
POST /locations crea una sede e la mette in coda per l'invio a tutte le piattaforme di pubblicazione del tuo piano, oppure solo a enabledSiteIds se passi quel campo. Ogni campo va inserito in input. 10 campi sono obbligatori; gli altri sono facoltativi e le piattaforme di pubblicazione mostrano vuoti quelli che ometti.
Parametri
Sono disponibili anche i campi facoltativi elencati nella pagina di riferimento: categorie GCID di Google, URL dei social, coordinate, contatto del proprietario, metodi di pagamento, area di servizio e altro.
curl -X POST https://listingsapi.com/api/v4/locations \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "name": "Acme Kitchen", "description": "Acme Kitchen is a family-owned neighborhood restaurant in downtown New York serving wood-fired pizza, hand-rolled pasta, and a short seasonal menu built around produce from nearby growers. Our dining room seats forty, takeout and delivery run until close, and the bar pours regional wine and beer on tap.", "storeId": "ACME01", "street": "123 Jump Street", "city": "New York", "stateIso": "NY", "postalCode": "10013", "countryIso": "US", "phone": "6443859313", "bizUrl": "https://acmekitchen.example.com", "subCategoryId": 1432, "businessHours": [ { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] }, { "day": "SUNDAY", "type": "CLOSED", "slots": [] } ], "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true } ] }}'{ "data": { "createLocation": { "clientMutationId": null, "success": true, "errors": null, "location": { "id": "TG9jYXRpb246MTgwMDI5MA==", "databaseId": 1800290, "storeId": "ACME01", "name": "Acme Kitchen", "city": "New York", "countryIso": "US", "status": "PENDING", "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true, "name": null, "submissionStatus": null } ], "discoveredPlaceActionLinks": [] } } }}Conserva entrambi gli ID: id in base64 è necessario per ogni scrittura successiva, mentre databaseId è il numero che vedi nelle dashboard e nei webhook. status parte da PENDING e avanza senza un'ulteriore chiamata API. I valori name e submissionStatus di un link di azione del luogo rimangono null finché il link non viene sincronizzato con Google. locations.add() di Python valida i campi obbligatori localmente e solleva ValidationError prima di qualsiasi chiamata di rete; locations.create(input) accetta invece un dict camelCase grezzo.
Aggiorna una sede
POST /locations/update è un aggiornamento parziale: input.id è l'unico campo obbligatorio e vengono modificati solo i campi che invii. La stessa chiamata corregge un numero di telefono o riscrive gli orari settimanali. Sia locations.update() di Python sia updateLocation() di Node accettano un unico oggetto camelCase.
Parametri
Oltre ai campi facoltativi elencati nella pagina di riferimento.
curl -X POST https://listingsapi.com/api/v4/locations/update \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "phone": "5125550199", "bizUrl": "https://acmekitchen.example.com", "primaryGbpSiteCategoryId": "gcid:restaurant", "businessHours": [ { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] }, { "day": "SUNDAY", "type": "CLOSED", "slots": [] } ] }}'{ "data": { "updateLocation": { "clientMutationId": null, "success": true, "errors": null, "location": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "phone": "5125550199", "status": "APPROVED", "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.example.com", "isPreferred": true, "name": null, "submissionStatus": null } ], "discoveredPlaceActionLinks": [] } } }}La risposta restituisce la sede aggiornata, così puoi verificare che i nuovi valori siano stati applicati. Ogni aggiornamento restituisce l'intero insieme attuale di placeActionLinks della sede. Per modificare l'URL di un link, invia un delete per il vecchio link e un add per quello nuovo nella stessa richiesta.
Archivia le sedi
POST /locations/archive pianifica l'archiviazione di una o più sedi. L'archiviazione non è immediata: ogni sede passa a ARCHIVE_SCHEDULED, rimane attiva e soggetta a fatturazione fino alla fine del ciclo di fatturazione corrente e viene archiviata quando viene generata la fattura successiva. Usa questa chiamata per disattivare in blocco le filiali chiuse.
Parametri
curl -X POST https://listingsapi.com/api/v4/locations/archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="] }}'{ "data": { "archiveLocations": { "clientMutationId": null, "bulkEditTaskId": null, "errors": null, "success": null, "result": [ { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null }, { "locationId": "TG9jYXRpb246MTgwMDI5MA==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null } ] } }}Il valore di success al livello principale è null; leggi invece il valore di success di ogni singola voce result[], così un id non valido non blocca il resto del batch.
Mentre l'archiviazione è pianificata, archivalScheduledAt della sede viene impostato nei risultati degli elenchi e delle ricerche. Una sede archiviata non può essere aggiornata finché non viene riattivata.
Annulla l'archiviazione pianificata
POST /locations/cancel_archive riporta le sedi da ARCHIVE_SCHEDULED a ACTIVE e la fatturazione continua. Funziona solo mentre il ciclo di fatturazione è ancora aperto; una volta che una sede è stata completamente archiviata, questa chiamata restituisce 404 per quella sede.
Parametri
curl -X POST https://listingsapi.com/api/v4/locations/cancel_archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="] } }'{ "data": { "cancelLocationsArchive": { "clientMutationId": null, "bulkEditTaskId": null, "errors": null, "success": null, "result": [ { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ACTIVE", "success": true, "errors": null } ] } }}Ogni voce result[] conferma che la sede è tornata a ACTIVE. Entrambi gli SDK richiedono 2 argomenti posizionali aggiuntivi, un tipo di selezione e un identificatore changedBy, che inviano insieme a locationIds; il body REST richiede solo gli id.
Carica foto per una sede
POST /locations/photos allega immagini tramite URL pubblico: tu ospiti l'immagine e l'API la recupera e la memorizza. Una sede conserva un LOGO, un COVER e un numero qualsiasi di foto ADDITIONAL. Caricare nuovamente un logo o un'immagine di copertina sostituisce quello esistente.
Parametri
curl -X POST https://listingsapi.com/api/v4/locations/photos \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photos": [ { "photo": "https://cdn.example.com/jenny-home/logo.png", "type": "LOGO" }, { "photo": "https://cdn.example.com/jenny-home/storefront.jpg", "type": "ADDITIONAL" } ] }}'{ "data": { "addLocationPhotos": { "clientMutationId": null, "success": true, "request": {}, "photos": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "databaseId": 5878931, "type": "LOGO", "starred": false, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.png" }, { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": false, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg" } ], "errors": null } }}Conserva id di ogni foto; gli endpoint per contrassegnarla come preferita e rimuoverla lo richiedono. Questo è il formato della risposta diretta per un batch piccolo. Un batch grande viene invece messo in coda: photos viene restituito vuoto e request.requestId viene impostato, quindi interroghi periodicamente l'endpoint successivo con quell'id. La soglia è configurata sul server, quindi scegli il ramo di esecuzione in base alla presenza di request.requestId.
Controllare lo stato del caricamento collettivo di foto
GET /locations/photos/requests/{requestId} verifica periodicamente un caricamento in coda finché non raggiunge uno stato definitivo, poi riporta l'esito per ogni immagine. Usalo per gestire una barra di avanzamento e mostrare esattamente quali file sono stati rifiutati e perché.
Parametri
curl https://listingsapi.com/api/v4/locations/photos/requests/e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationPhotosUploadStatus": { "requestId": "e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a", "status": "SUCCESS", "photos": [ { "mediaId": "TWVkaWFGaWxlOjkxODI3", "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "SUCCESS", "errorMessage": null }, { "mediaId": null, "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ERROR", "errorMessage": "Image dimensions below the minimum 250x250." } ] } }}Lo stato del job indica solo che l'elaborazione è terminata, non che ogni immagine sia stata caricata correttamente. Attendi qualche secondo tra una verifica e l'altra; un ciclo continuo di richieste porta rapidamente a 429.
Recuperare le foto di una sede
GET /locations/{locationId}/photos restituisce tutti i file multimediali di una sede come un semplice array: logo, copertina e immagini aggiuntive, ciascuno con il proprio url pubblico, thumbnailUrl, la categoria, il flag starred e il valore viewsCount relativo all'intera durata. Chiamalo prima di un caricamento per vedere quale posizione è vuota e raccogliere gli ID per le chiamate che contrassegnano con una stella e rimuovono le foto.
Parametri
curl https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/photos \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "mediaFilesOfLocation": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "categoryName": "Logo", "categoryId": "4", "fileType": "image", "starred": false, "viewsCount": 0, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.jpg", "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_f47d62a6.jpg" }, { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "categoryName": "Additional", "categoryId": "12", "fileType": "image", "starred": true, "viewsCount": 143, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg", "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_ef4ffb12.jpg" } ] }}categoryName è Logo, Cover o Additional. Usa thumbnailUrl per una griglia e url per la visualizzazione a dimensione intera. Una sede senza contenuti multimediali restituisce un array vuoto, non un errore.
Contrassegnare con una stella le foto di una sede
POST /locations/photos/star contrassegna le foto aggiuntive per il posizionamento prioritario sulle piattaforme di pubblicazione che supportano le immagini in evidenza. Solo le foto ADDITIONAL possono essere contrassegnate con una stella, fino a un massimo di 4 per sede. La rimozione della stella funziona allo stesso modo: il flag starred nel corpo della richiesta determina l'azione.
Parametri
curl -X POST https://listingsapi.com/api/v4/locations/photos/star \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="], "starred": true }}'{ "data": { "starUnstarLocationPhotos": { "success": true, "photos": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": true } ], "error": null } }}La chiave della risposta è starUnstarLocationPhotos per entrambi i percorsi e contiene un singolo error, non un array errors. Una quinta foto contrassegnata con una stella fa fallire l'intera chiamata con SY15012; il controllo conta le foto già contrassegnate con una stella più quelle incluse in questa richiesta.
Rimuovere foto da una sede
POST /locations/photos/remove scollega le foto aggiuntive tramite l'ID del contenuto multimediale. Usalo per rimuovere immagini obsolete della facciata del negozio o annullare un'importazione collettiva errata. Solo le foto ADDITIONAL possono essere rimosse; per sostituire un logo o una copertina, carica una nuova immagine con quel valore di type.
Parametri
curl -X POST https://listingsapi.com/api/v4/locations/photos/remove \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="] }}'{ "data": { "removeLocationPhotos": { "clientMutationId": null, "removedCount": 1, "errors": null } }}removedCount indica quanti file sono stati effettivamente scollegati, quindi 0 con errors: null significa che nessuno di quegli ID era associato a questa sede. La rimozione scollega il file; non lo elimina necessariamente dalla tua libreria multimediale.
Errori che potresti incontrare
Le operazioni di scrittura possono anche restituire HTTP 200 con success: false e il dettaglio in data.<operation>.errors. L'elenco completo si trova in codici di errore.
Passaggi successivi
- Aggiungi una sede, Aggiorna una sede, Carica le foto di una sede e Trova e archivia le sedi: le guide operative basate su questi endpoint.
- Cerca paesi e categorie: i valori di
countryIso,stateIsoesubCategoryIdnecessari per un'operazione di creazione. - Link di azione per i luoghi: il modello completo per i pulsanti di azione di Google nelle operazioni di creazione e aggiornamento.
- Sedi: la pagina principale della risorsa e i riferimenti degli SDK per Python e Node.
- I metodi per le foto hanno pagine dedicate nella documentazione degli SDK: Foto in Python e Foto in Node.