حالات الاستخدام

أمثلة تطبيقية متكاملة بلغة TypeScript توضّح كيفية الجمع بين موارد listingsapi-js لبناء تكاملات فعلية.

هذه الأمثلة هي مسارات عمل تستخدم موارد متعددة، وهي نصوص برمجية تحتاج إليها عندما لا تكفي صفحة توثيق دالة واحدة. كل مثال هو برنامج TypeScript كامل قابل للتشغيل، يجمع موردين أو أكثر في برنامج يمكنك نشره فعليًا. ثبّت إصدارًا محددًا من SDK (npm install listingsapi-js@0.3.2)، وصدّر LISTINGSAPI_KEY باستخدام مفتاح من لوحة التحكم، ثم يمكنك تشغيل هذه البرامج كما هي باستخدام npx tsx <file>.ts.

إذا كنت تستخدم SDK لأول مرة، فاقرأ أولًا نظرة عامة على Node SDK. فهي تتناول إنشاء العميل والمصادقة والأخطاء وتقسيم النتائج إلى صفحات، وهي أمور يفترض كل مثال أدناه معرفتك بها.

1. البدء السريع: جلب المواقع وطباعة أسمائها

أبسط تكامل عملي: إجراء المصادقة، وجلب صفحة واحدة من المواقع، وطباعة أسمائها.

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);}}

بدون fetchAll، يعيد fetchAllLocations كائنًا يحتوي على locations وpageInfo وحمولة البيانات raw. مرّر pageInfo.endCursor بوصفه قيمة after للحصول على الصفحة التالية.

2. التصدير الجماعي إلى JSON

اجلب جميع المواقع في الحساب بالتنقل تلقائيًا بين صفحات النتائج، واكتب النتائج على القرص.

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 تلقائيًا بين الصفحات باستخدام مؤشرات الصفحات ويعيد Location[] مسطحًا. عند التعامل مع 10 000 موقع أو أكثر، أبقِ pageSize على القيمة 100 (القيمة الافتراضية) للحفاظ على سرعة الطلبات الفردية.

3. إعداد موقع جديد

ابحث عن معرّف الفئة المناسب باستخدام fetchSubcategories، ثم أنشئ الموقع. يتوقع createLocation أن تكون قيمة subCategoryId هي databaseId لفئة فرعية. تتطلب API أيضًا وصفًا لا يقل طوله عن 200 حرف.

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);

عند فشل التحقق من صحة البيانات (حقل مفقود أو وصف قصير أو فئة غير صالحة)، يُرمى استثناء ValidationError مع تفاصيل أخطاء المنصة في err.errors، لذا لا توجد علامة success لفحصها.

4. نشر منشور على Google وFacebook لعدة مواقع دفعة واحدة

ينشر bulkPublish منشورًا واحدًا على كلتا المنصتين لعدة مواقع في استدعاء واحد. ويتحقق من صحة حمولة البيانات من جهة العميل، ويُعِدّ الرسالة لكل منصة، ويرمّز معرّفات المواقع الرقمية نيابةً عنك.

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);

القيمة الافتراضية لـ sites هي ['GOOGLE', 'FACEBOOK']. مرّر خريطة بقيمة لكل منصة بوصفها message ({ GOOGLE: '...', FACEBOOK: '...' }) عندما يلزم اختلاف النص. للحملات الفردية ذات الخيارات المحددة الأنواع، استخدم createAnnouncement أو createEvent أو createOffer بدلًا من ذلك.

5. مراقبة التقييمات: تحديد التقييمات المنخفضة

افحص كل موقع بحثًا عن التقييمات ذات النجمة الواحدة والنجمتين، وسجّل التقييمات التي تحتاج إلى اهتمام.

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. الرد على التقييمات التي لم يُردّ عليها

أرسل ردًا تلقائيًا على كل تقييم ليس له رد حتى الآن. استخدم قالبًا أو استبدل buildResponse باستدعاء لنموذج لغوي كبير.

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. تقرير التحليلات: Google وBing وFacebook وإحصاءات التقييمات

اجلب تحليلات الملف التعريفي وإحصاءات التقييمات لموقع واحد بالتوازي، واطبع ملخصًا من صفحة واحدة.

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));

لاحظ اختلاف المعاملات: تستخدم تحليلات المنصات fromDate/toDate، بينما تستخدم تحليلات التقييمات startDate/endDate.

8. تدقيق إدراجات الأنشطة التجارية: العثور على الملفات التعريفية غير المتزامنة والمكررة

افحص موقعًا واحدًا بحثًا عن إدراجات الأنشطة التجارية المميزة غير المتزامنة وعن الملفات التعريفية المكررة التي عثر عليها ListingsAPI، ثم ضع علامة على الملفات المكررة.

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');}

لإجراء فحص على مستوى الحساب بأكمله، يعيد fetchAllDuplicateListings({ page: 1 }) ملخصًا مجمعًا للتكرارات عبر جميع المواقع.

9. الحسابات المتصلة: خطوات الربط بـ Google

اربط حساب Google مرة واحدة، وطابق إدراجات الأنشطة التجارية فيه مع مواقعك، وأكّد المطابقات المقترحة.

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');

لربط موقع واحد بدلًا من ذلك، استخدم getOauthConnectUrl(locationId, 'GOOGLE', successUrl, errorUrl)، واستخدم connectListing(locationId, listingId, accountId) لربط إدراج نشاط تجاري واحد مباشرةً.

10. البحث عن المواقع وتحديث حقل فيها دفعة واحدة

ابحث عن المواقع المطابقة لاستعلام، ثم حدّث حقلًا في كل نتيجة، مثل تعيين رقم هاتف جديد للمواقع في مدينة بأكملها.

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');

الخطوات التالية