Cas d’utilisation

Exemples complets en TypeScript montrant comment combiner les ressources de listingsapi-js pour créer des intégrations concrètes.

Ces exemples présentent des procédures qui combinent plusieurs ressources, utiles lorsqu'une page consacrée à une seule méthode ne suffit pas. Chacun est un programme TypeScript complet et exécutable qui combine au moins deux ressources pour un usage concret en production. Installez une version fixe du SDK (npm install listingsapi-js@0.3.2), exportez LISTINGSAPI_KEY avec une clé de votre tableau de bord, puis exécutez ces programmes tels quels avec npx tsx <file>.ts.

Si vous découvrez le SDK, lisez d'abord la présentation du Node SDK. Elle couvre la création du client, l'authentification, les erreurs et la pagination, des notions supposées connues dans tous les exemples ci-dessous.

1. Démarrage rapide : récupérer et afficher les établissements

L'intégration minimale : authentifiez-vous, récupérez une page d'établissements et affichez leurs noms.

01_quickstart.ts
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); // reads LISTINGSAPI_KEY from env const page = await client.fetchAllLocations({ first: 5 });if (!Array.isArray(page)) {for (const loc of page.locations) {  console.log(loc.name + ' - ' + loc.city);}}

Sans fetchAll, fetchAllLocations renvoie un objet contenant locations, pageInfo et les données raw. Passez pageInfo.endCursor comme valeur de after pour obtenir la page suivante.

2. Exporter en masse au format JSON

Parcourez automatiquement toutes les pages d'établissements du compte et enregistrez les résultats sur disque.

02_bulk_export.ts
import { writeFileSync } from 'fs';import { ListingsAPI } from 'listingsapi-js';import type { Location } from 'listingsapi-js'; const client = new ListingsAPI(); const locations = (await client.fetchAllLocations({fetchAll: true,pageSize: 100,})) as Location[]; writeFileSync('locations.json', JSON.stringify(locations, null, 2));console.log('Exported ' + locations.length + ' locations to locations.json');

fetchAll: true parcourt automatiquement les pages à l'aide des curseurs et renvoie un Location[] sans imbrication. Pour 10 000 établissements ou plus, conservez pageSize à 100 (la valeur par défaut) afin que chaque requête reste rapide.

3. Ajouter un nouvel établissement

Recherchez l'identifiant de catégorie approprié avec fetchSubcategories, puis créez l'établissement. createLocation attend le databaseId d'une sous-catégorie comme valeur de subCategoryId. L'API exige également une description d'au moins 200 caractères.

03_onboarding.ts
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); const categories = await client.fetchSubcategories();const dentist = categories.find((c) => c.name === 'Dentist');if (!dentist) throw new Error('Category not found'); const description ='Acme Dental is a family-owned dental practice in downtown New York ' +'offering preventive care, cosmetic dentistry, orthodontics, and ' +'emergency appointments. Our team has served the neighborhood for over ' +'twenty years with same-week scheduling and weekend hours.'; const created = await client.createLocation({name:          'Acme Dental',storeId:       'ACME-NY-001',street:        '123 Jump Street',city:          'New York',stateIso:      'NY',postalCode:    '10013',countryIso:    'US',phone:         '6443859313',subCategoryId: dentist.databaseId,description:   description,}); console.log('Created location', created.location?.id);

Un échec de validation (champ manquant, description trop courte, catégorie incorrecte) déclenche une exception ValidationError contenant les erreurs de la plateforme dans err.errors ; il n'y a donc aucun indicateur success à vérifier.

4. Publier en masse une publication sur Google et Facebook

bulkPublish publie une même publication sur les deux sites pour plusieurs établissements en un seul appel. Cette méthode valide les données côté client, prépare le message pour chaque site et encode les identifiants numériques des établissements pour vous.

04_bulk_publish.ts
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); const result = await client.bulkPublish({name:        'Holiday hours',locationIds: [16808, 16809, 16810],message:     'Open until 10pm through the holidays!',mediaUrl:    'https://cdn.example.com/holiday.jpg',ctaType:     'LEARN_MORE',ctaUrl:      'https://example.com/holiday-hours',}); console.log('Campaign:', result.socialPost?.id, result.socialPost?.status); // Publishing is asynchronous; poll the bulk campaign for per-location statusconst bulk = await client.fetchBulkPost(result.socialPost.id);console.log(bulk);

La valeur par défaut de sites est ['GOOGLE', 'FACEBOOK']. Passez une table de correspondance par site comme valeur de message ({ GOOGLE: '...', FACEBOOK: '...' }) lorsque le texte doit différer. Pour des campagnes individuelles avec des options typées, utilisez plutôt createAnnouncement, createEvent ou createOffer.

5. Suivre les avis : signaler les notes faibles

Recherchez les avis à 1 et 2 étoiles pour chaque établissement et consignez ceux qui nécessitent votre attention.

05_review_monitoring.ts
import { ListingsAPI, APIError } from 'listingsapi-js';import type { Interaction, Location } from 'listingsapi-js'; const client = new ListingsAPI(); const locations = (await client.fetchAllLocations({ fetchAll: true })) as Location[]; for (const loc of locations) {let reviews: Interaction[];try {  reviews = (await client.fetchInteractions(loc.id, {    fetchAll:      true,    startDate:     '2026-01-01',    ratingFilters: [1, 2],  })) as Interaction[];} catch (err) {  if (err instanceof APIError) {    console.error(loc.name + ': ' + err.statusCode);    continue;  }  throw err;} for (const review of reviews) {  const who = review.reviewerName ?? 'Anonymous';  console.log('NEEDS ATTENTION [' + loc.name + '] ' + who + ' (' + review.rating + ' stars)');}}

6. Répondre aux avis sans réponse

Répondez automatiquement à chaque avis qui n'a pas encore de réponse. Utilisez un modèle de réponse ou remplacez buildResponse par un appel à un LLM.

06_bulk_respond.ts
import { ListingsAPI } from 'listingsapi-js';import type { Interaction } from 'listingsapi-js'; const client = new ListingsAPI();const LOCATION_ID = 16808; function buildResponse(review: Interaction): string {const name = review.reviewerName ?? 'there';if ((review.rating ?? 5) <= 2) {  return 'Hi ' + name + ', we are sorry to hear about your experience. Our team will reach out shortly.';}return 'Hi ' + name + ', thank you for the kind words. We look forward to seeing you again!';} const reviews = (await client.fetchInteractions(LOCATION_ID, {fetchAll:  true,startDate: '2026-01-01',})) as Interaction[]; let responded = 0;for (const review of reviews) {if (review.responses && review.responses.length > 0) continue;await client.respondToReview(review.id, buildResponse(review));responded++;}console.log('Responded to ' + responded + ' reviews');

7. Rapport d'analyse : Google, Bing, Facebook et statistiques des avis

Récupérez en parallèle les données d'analyse des profils et les statistiques des avis pour un seul établissement, puis affichez une synthèse d'une page.

07_analytics_report.ts
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI();const LOCATION_ID = 16808;const range = { fromDate: '2026-01-01', toDate: '2026-06-30' }; const [google, bing, facebook, reviewOverview] = await Promise.all([client.fetchGoogleAnalytics(LOCATION_ID, range),client.fetchBingAnalytics(LOCATION_ID, range),client.fetchFacebookAnalytics(LOCATION_ID, range),client.fetchReviewAnalyticsOverview(LOCATION_ID, {  startDate: range.fromDate,  endDate:   range.toDate,}),]); console.log('Google insights:',  JSON.stringify(google));console.log('Bing insights:',    JSON.stringify(bing));console.log('Facebook insights:', JSON.stringify(facebook));console.log('Review overview:',  JSON.stringify(reviewOverview));

Notez la différence entre les paramètres : les analyses des plateformes utilisent fromDate/toDate, tandis que les analyses des avis utilisent startDate/endDate.

8. Auditer les fiches : trouver les profils non synchronisés et les doublons

Pour un établissement, recherchez les fiches premium non synchronisées et les profils en double détectés par ListingsAPI, puis marquez les doublons.

08_listings_audit.ts
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI();const LOCATION_ID = 16808; const [premium, voice, duplicates] = await Promise.all([client.fetchPremiumListings(LOCATION_ID),client.fetchVoiceListings(LOCATION_ID),client.fetchDuplicateListings(LOCATION_ID),]); for (const l of premium) {if (l.syncStatus !== 'SYNCED') {  console.log('Not synced: ' + l.site + ' - ' + l.syncStatus);}} console.log('Voice listings: ' + voice.length);console.log('Duplicates detected: ' + duplicates.length); const ids = duplicates.map((d) => d.id).filter((id): id is string => typeof id === 'string'); if (ids.length > 0) {await client.markListingsAsDuplicate(LOCATION_ID, ids);console.log('Marked ' + ids.length + ' listings as duplicate');}

Pour une vérification à l'échelle du compte, fetchAllDuplicateListings({ page: 1 }) renvoie un récapitulatif des doublons de tous les établissements.

9. Comptes connectés : la procédure Google

Connectez un compte Google une seule fois, associez ses fiches à vos établissements et confirmez les correspondances suggérées.

09_connected_accounts.ts
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); // 1. Get an OAuth URL and send the account owner thereconst connect = await client.connectGoogleAccount('https://example.com/connected','https://example.com/connect-error',);console.log('Send the owner to:', JSON.stringify(connect)); // 2. After the owner completes OAuth, list connected accountsconst accounts = await client.fetchConnectedAccounts({ page: 1, perPage: 25 });console.log(JSON.stringify(accounts)); // 3. Trigger matching between the account's listings and your locationsconst accountId = 'connected-account-uuid';await client.triggerConnectedAccountMatches([accountId]); const suggestions = await client.fetchConnectionSuggestions(accountId, {page: 1,perPage: 25,});console.log(JSON.stringify(suggestions)); // 4. Confirm the matches you agree withawait client.confirmConnectedAccountMatches(['match-record-id-1','match-record-id-2',]);console.log('Matches confirmed');

Pour connecter plutôt un seul établissement, utilisez getOauthConnectUrl(locationId, 'GOOGLE', successUrl, errorUrl) et connectListing(locationId, listingId, accountId) pour associer directement une fiche.

10. Rechercher des établissements et mettre à jour un champ en masse

Recherchez les établissements correspondant à une requête, puis mettez à jour un champ pour chaque résultat, par exemple pour appliquer un nouveau numéro de téléphone aux établissements d'une ville.

10_bulk_update.ts
import { ListingsAPI } from 'listingsapi-js';import type { Location } from 'listingsapi-js'; const client = new ListingsAPI(); const matches = (await client.searchLocations('Brooklyn', { fetchAll: true })) as Location[]; let updated = 0;for (const loc of matches) {await client.updateLocation({  id:    loc.id,  phone: '7185550100',});updated++;}console.log('Updated phone on ' + updated + ' Brooklyn locations');

Pour aller plus loin

  • Établissements : référence complète des méthodes avec tableaux des paramètres
  • Avis : analyses, paramètres, analyse des expressions
  • Fiches : premium, recherche vocale, doublons
  • Analyses : statistiques Google, Bing et Facebook
  • Gestion des erreurs : sous-classes de APIError et stratégie de nouvelle tentative