Établissements
Endpoints des établissements, par ordre d’usage : exemples cURL, Python, Node ; liste, recherche, ID/code magasin, création, mise à jour, archivage, photos
Les fiches, les avis, les publications et les données d’analyse sont tous associés à un identifiant d’établissement. La ressource des établissements comporte treize endpoints : quatre en lecture pour trouver les établissements, quatre en écriture pour les créer, les modifier et les archiver, et cinq pour gérer les photos. Une clé Read (lecture) permet les opérations de lecture. Chaque POST nécessite une clé Write (écriture) ; l’utilisation d’une clé Read pour une opération d’écriture renvoie le statut HTTP 400 avec SY90016.
Chaque identifiant de ce guide est un identifiant Relay en base64, par exemple TG9jYXRpb246MTgwMDI4OQ== (qui donne Location:1800289 une fois décodé). Les deux SDK acceptent également le databaseId numérique et l’encodent pour vous.
Les endpoints en un coup d’œil
Lister tous les établissements
GET /locations permet de parcourir tous les établissements du compte par pages, du plus récent au plus ancien. Il s’agit d’une connexion paginée par curseur : edges[].node contient chaque enregistrement, et pageInfo.endCursor fournit la valeur du paramètre after pour la page suivante. Utilisez cet endpoint pour synchroniser l’ensemble du compte avec votre propre système.
Paramètres
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 } } }}Consultez pageInfo.hasNextPage et arrêtez-vous lorsque sa valeur est false. Le nombre d’éléments de la page est indiqué par pageInfo.total. Le id du nœud est l’identifiant attendu par tous les autres endpoints.
Rechercher des établissements
GET /locations/search recherche un mot-clé dans le nom de l’établissement, l’adresse postale, la ville et l’identifiant du magasin. Il s’agit d’une connexion paginée par curseur dont les structures edges[].node et pageInfo.endCursor sont identiques à celles de la liste. Utilisez cet endpoint pour un sélecteur avec suggestions à la saisie, ou pour vérifier qu’un établissement existe avant de le créer.
Paramètres
Les deux SDK acceptent également une liste fields pour limiter la recherche à des champs précis, par exemple uniquement 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 tableau edges vide signifie qu’aucune correspondance n’a été trouvée. Le nœud contient l’adresse complète, la catégorie et le statut approved ; une ligne de résultat ne nécessite donc aucune seconde récupération.
Récupérer des établissements par leurs identifiants
GET /locations-by-ids renvoie l'enregistrement complet de chaque établissement d'un lot d'identifiants base64 en un seul appel, ce qui utilise moins de requêtes qu'un appel par établissement. Les établissements actifs comme les établissements archivés sont retrouvés. Le résultat est un simple tableau, pas une connexion. Utilisez cet appel pour récupérer les enregistrements correspondant aux identifiants provenant de la charge utile d'un webhook ou de votre propre stockage.
Paramètres
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": [] } ] }}Les identifiants qui n'existent pas dans votre compte sont ignorés silencieusement au lieu de provoquer une erreur. Comparez le nombre d'enregistrements renvoyés au nombre d'identifiants envoyés pour détecter les identifiants obsolètes.
Récupérer des établissements par leurs codes magasin
GET /locations-by-store-codes effectue la recherche inverse : il retrouve les enregistrements complets à partir de vos propres codes magasin. Il renvoie un simple tableau. Utilisez-le lorsque votre système de point de vente ou votre registre de franchises identifie les établissements par leur code magasin et que vous n'avez jamais enregistré nos identifiants.
Paramètres
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" } ] }}Faites correspondre le champ storeId de chaque objet à une entrée de votre registre. Les codes sans correspondance sont absents. Un établissement ne possède un code magasin que si storeId a été défini lors de sa création ou de sa mise à jour.
Créer un établissement
POST /locations crée un établissement et le place dans la file d'attente pour soumission à chaque plateforme de publication de votre forfait, ou uniquement à enabledSiteIds lorsque vous transmettez ce champ. Tous les champs doivent être placés sous input. 10 champs sont obligatoires ; les autres sont facultatifs, et les plateformes de publication affichent comme vides ceux que vous omettez.
Paramètres
S'y ajoutent les champs facultatifs répertoriés sur la page de référence : catégories GCID Google, URL des réseaux sociaux, coordonnées géographiques, coordonnées du propriétaire, moyens de paiement, zone desservie, etc.
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": [] } } }}Conservez les 2 identifiants : id en base64 est nécessaire pour toutes les écritures ultérieures, et databaseId est le numéro affiché dans les tableaux de bord et les webhooks. status vaut initialement PENDING et évolue sans appel API supplémentaire. Les champs name et submissionStatus d'un lien d'action d'établissement restent à null jusqu'à sa synchronisation avec Google. En Python, locations.add() valide les champs obligatoires localement et lève ValidationError avant tout appel réseau ; locations.create(input) accepte à la place un dictionnaire camelCase brut.
Mettre à jour un établissement
POST /locations/update effectue une mise à jour partielle : input.id est le seul champ obligatoire et seuls les champs que vous envoyez sont modifiés. Le même appel permet de corriger un numéro de téléphone ou de réécrire les horaires hebdomadaires. locations.update() en Python et updateLocation() en Node acceptent tous deux un seul objet camelCase.
Paramètres
Ainsi que les champs facultatifs répertoriés sur la page de référence.
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 réponse renvoie l'établissement mis à jour afin que vous puissiez confirmer l'application des nouvelles valeurs. Chaque mise à jour renvoie l'ensemble complet des placeActionLinks actuels de l'établissement. Pour modifier l'URL d'un lien, envoyez un delete pour l'ancien lien et un add pour le nouveau dans la même requête.
Archiver des établissements
POST /locations/archive programme l'archivage d'un ou de plusieurs établissements. L'archivage n'est pas immédiat : chaque établissement passe à l'état ARCHIVE_SCHEDULED, reste actif et facturé jusqu'à la fin du cycle de facturation en cours, puis est archivé lors de la génération de la facture suivante. Utilisez cet appel pour retirer par lot les établissements fermés.
Paramètres
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 } ] } }}Le champ success de premier niveau vaut null ; lisez plutôt le champ success propre à chaque entrée result[], afin qu'un identifiant incorrect ne bloque pas le reste du lot.
Tant que l'archivage est programmé, le champ archivalScheduledAt de l'établissement est défini dans les résultats de liste et de recherche. Un établissement archivé ne peut pas être mis à jour tant qu'il n'a pas été réactivé.
Annuler un archivage programmé
POST /locations/cancel_archive fait repasser les établissements à l'état ARCHIVE_SCHEDULED à l'état ACTIVE, et la facturation se poursuit. Cet appel ne fonctionne que tant que le cycle de facturation est encore ouvert ; une fois un établissement entièrement archivé, il renvoie 404 pour cet établissement.
Paramètres
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 } ] } }}Chaque entrée result[] confirme que l'établissement est revenu à l'état ACTIVE. Les deux SDK exigent deux arguments positionnels supplémentaires, un type de sélection et un identifiant changedBy, qu'ils envoient avec locationIds ; le corps de la requête REST ne nécessite que les identifiants.
Importer des photos dans un établissement
POST /locations/photos joint des images à partir d'une URL publique : vous hébergez l'image, l'API la récupère et la stocke. Un établissement conserve un LOGO, un COVER et un nombre quelconque de photos ADDITIONAL. Une nouvelle importation d'un logo ou d'une photo de couverture remplace l'élément existant.
Paramètres
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 } }}Conservez le id de chaque photo ; les points de terminaison permettant de marquer une photo d'une étoile et de la supprimer l'utilisent. Il s'agit du format de réponse directe pour un petit lot. Un lot volumineux est en revanche mis en file d'attente : photos est renvoyé vide et request.requestId est défini, et vous interrogez périodiquement le point de terminaison suivant avec cet identifiant. Le seuil est configuré côté serveur ; adaptez donc le traitement selon la présence de request.requestId.
Vérifier l’état d’un envoi groupé de photos
GET /locations/photos/requests/{requestId} interroge régulièrement l’état d’un envoi en file d’attente jusqu’à ce qu’il atteigne un état définitif, puis indique le résultat pour chaque image. Utilisez-le pour alimenter une barre de progression et afficher précisément les fichiers rejetés et les raisons de leur rejet.
Paramètres
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." } ] } }}L’état de la tâche indique uniquement que le traitement est terminé, et non que toutes les images ont bien été ajoutées. Espacez les interrogations de quelques secondes ; une boucle sans pause risque de vous faire atteindre rapidement 429.
Récupérer les photos d’un établissement
GET /locations/{locationId}/photos renvoie tous les fichiers multimédias d’un établissement sous forme de tableau simple : logo, couverture et images supplémentaires, chacun avec son url public, son thumbnailUrl, sa catégorie, son indicateur starred et son viewsCount cumulé sur toute sa durée de vie. Appelez-le avant un envoi pour voir quel emplacement est vide et recueillir les identifiants nécessaires aux appels d’attribution d’étoiles et de suppression.
Paramètres
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 vaut Logo, Cover ou Additional. Utilisez thumbnailUrl pour une grille et url pour l’affichage en taille réelle. Pour un établissement sans fichiers multimédias, l’endpoint renvoie un tableau vide, et non une erreur.
Attribuer une étoile aux photos d’un établissement
POST /locations/photos/star marque les photos supplémentaires pour leur donner un placement prioritaire sur les plateformes de publication qui prennent en charge la mise en avant d’images. Seules les photos ADDITIONAL peuvent recevoir une étoile, dans la limite de 4 par établissement. Le retrait d’une étoile fonctionne de la même manière : l’indicateur starred dans le corps de la requête détermine l’action effectuée.
Paramètres
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 clé de réponse est starUnstarLocationPhotos pour les deux chemins et contient un seul error, et non un tableau errors. Une cinquième photo avec une étoile fait échouer l’ensemble de l’appel avec SY15012 ; la vérification compte les photos ayant déjà une étoile ainsi que celles de cette requête.
Supprimer des photos d’un établissement
POST /locations/photos/remove dissocie les photos supplémentaires à partir de leur identifiant de fichier multimédia. Utilisez-le pour retirer des images de devanture obsolètes ou annuler un import groupé incorrect. Seules les photos ADDITIONAL peuvent être supprimées ; pour remplacer un logo ou une couverture, envoyez une nouvelle image avec ce type.
Paramètres
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 indique le nombre de fichiers effectivement dissociés ; 0 avec errors: null signifie donc qu’aucun de ces identifiants n’était associé à cet établissement. La suppression dissocie le fichier ; elle ne le supprime pas nécessairement de votre bibliothèque multimédia.
Erreurs que vous rencontrerez
Les opérations d’écriture peuvent également renvoyer HTTP 200 avec success: false et les détails dans data.<operation>.errors. La liste complète figure dans les codes d’erreur.
Étapes suivantes
- Ajouter un établissement, Mettre à jour un établissement, Importer des photos d’un établissement et Rechercher et archiver des établissements : les guides pratiques qui s’appuient sur ces endpoints.
- Rechercher des pays et des catégories : les valeurs
countryIso,stateIsoetsubCategoryIdnécessaires à une création. - Place Action Links : le modèle complet des boutons d’action Google lors de la création et de la mise à jour.
- Établissements : la page d’accueil de la ressource et les références des SDK Python et Node.
- Les méthodes relatives aux photos disposent de leurs propres pages de documentation SDK : Photos avec Python et Photos avec Node.