Casos de uso
Ejemplos completos en TypeScript que muestran cómo combinar los recursos de listingsapi-js en integraciones reales.
Estos ejemplos son flujos de trabajo que combinan varios recursos, el tipo de scripts que necesitas cuando la página de un solo método no basta. Cada uno es un programa TypeScript completo y ejecutable que combina dos o más recursos en una solución que podrías desplegar. Instala una versión fija del SDK (npm install listingsapi-js@0.3.2), exporta LISTINGSAPI_KEY con una clave de tu panel y podrás ejecutarlos sin cambios con npx tsx <file>.ts.
Si es la primera vez que usas el SDK, lee primero la descripción general del Node SDK. Explica la creación del cliente, la autenticación, los errores y la paginación, conceptos que todos los ejemplos siguientes dan por conocidos.
1. Inicio rápido: obtener e imprimir ubicaciones
La integración mínima viable: autentícate, recupera una página de ubicaciones e imprime sus nombres.
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);}}Sin fetchAll, fetchAllLocations devuelve un objeto con locations, pageInfo y los datos de raw. Pasa pageInfo.endCursor como after para obtener la página siguiente.
2. Exportación masiva a JSON
Recorre automáticamente todas las páginas de ubicaciones de la cuenta y escribe los resultados en disco.
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 recorre automáticamente las páginas mediante cursores y devuelve un Location[] plano. Para 10 000+ ubicaciones, mantén pageSize en 100 (el valor predeterminado) para que las solicitudes individuales sigan siendo rápidas.
3. Dar de alta una nueva ubicación
Busca el ID de categoría adecuado con fetchSubcategories y luego crea la ubicación. El databaseId de una subcategoría es lo que createLocation espera como subCategoryId. La API también requiere una descripción de al menos 200 caracteres.
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 fallo de validación (campo ausente, descripción demasiado corta, categoría incorrecta) lanza un ValidationError con las entradas de error de la plataforma en err.errors, por lo que no hay ningún indicador success que comprobar.
4. Publicar una publicación en Google y Facebook de forma masiva
bulkPublish publica una publicación en ambos sitios para varias ubicaciones en una sola llamada. Valida los datos en el cliente, genera el mensaje para cada sitio y codifica los ID numéricos de las ubicaciones por ti.
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);sites usa ['GOOGLE', 'FACEBOOK'] de forma predeterminada. Pasa un mapa por sitio como message ({ GOOGLE: '...', FACEBOOK: '...' }) cuando el texto deba ser diferente. Para campañas individuales con opciones tipadas, usa createAnnouncement, createEvent o createOffer en su lugar.
5. Supervisión de reseñas: marcar valoraciones bajas
Busca reseñas de 1 y 2 estrellas en todas las ubicaciones y registra las que necesiten atención.
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. Responder a reseñas sin respuesta
Responde automáticamente a todas las reseñas que aún no tengan respuesta. Usa una plantilla o sustituye buildResponse por una llamada a un LLM.
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. Informe de analítica: Google, Bing, Facebook y estadísticas de reseñas
Recupera en paralelo los datos de analítica del perfil y las estadísticas de reseñas de una sola ubicación e imprime un resumen de una página.
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));Ten en cuenta la diferencia entre los parámetros: la analítica de las plataformas usa fromDate/toDate, mientras que la analítica de reseñas usa startDate/endDate.
8. Auditoría de fichas: encontrar perfiles sin sincronizar y duplicados
Revisa una ubicación para encontrar fichas prémium que no estén sincronizadas y perfiles duplicados que ListingsAPI haya detectado; después, marca los duplicados.
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');}Para revisar toda la cuenta, fetchAllDuplicateListings({ page: 1 }) devuelve un resumen de los duplicados de todas las ubicaciones.
9. Cuentas conectadas: el flujo de Google
Conecta una cuenta de Google una sola vez, relaciona sus fichas con tus ubicaciones y confirma las correspondencias sugeridas.
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');Para conectar una sola ubicación, usa getOauthConnectUrl(locationId, 'GOOGLE', successUrl, errorUrl) y connectListing(locationId, listingId, accountId) para vincular una ficha directamente.
10. Buscar ubicaciones y actualizar un campo de forma masiva
Busca ubicaciones que coincidan con una consulta y luego actualiza un campo en cada resultado, por ejemplo, para asignar un nuevo número de teléfono a las ubicaciones de una ciudad.
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');Siguientes pasos
- Ubicaciones: referencia completa de métodos con tablas de parámetros
- Reseñas: analítica, configuración y análisis de frases
- Fichas: prémium, voz y duplicados
- Analítica: estadísticas de Google, Bing y Facebook
- Gestión de errores: subclases de
APIErrory estrategia de reintentos