نشر منشورات على Google وFacebook
انشر إعلانًا وفعالية وعرضًا، وجدول منشورًا واحدًا لمواقع متعددة، وتحقق من نشره على كل منصة، وأدر منشورات موقع، واستجب لإشعارات webhooks للمنشورات.
المنشور هو بطاقة مؤرخة على ملف الموقع في Google Business Profile أو صفحته على Facebook: إعلان أو فعالية أو عرض. ينشئه استدعاء واحد، وتُنفَّذ عملية النشر في الخلفية، ويخبرك استدعاء ثانٍ بما إذا كانت كل منصة قد قبلته.
تستخدم أنواع المنشورات الثلاثة جميعها POST /posts مع تغليف جسم الطلب داخل input. يُرفض جسم الطلب المسطح مع إرجاع 400. تنشئ حزم SDK الغلاف وتتحقق من صحة الحمولة قبل الإرسال.
1. نشر إعلان
يتضمن الإعلان رسالة وزرًا اختياريًا وصورة اختيارية.
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "Grand Opening Announcement", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="], "postType": "ANNOUNCEMENT", "postSites": ["GOOGLE"], "postMessage": [ { "site": "GOOGLE", "message": "We are now open! Visit us this week for our grand opening." } ], "postCta": [ { "site": "GOOGLE", "type": "LEARN_MORE", "url": "https://acmekitchen.example.com/opening" } ], "postMediaUrl": [ { "site": "GOOGLE", "url": "https://cdn.example.com/opening.jpg", "type": "IMAGE" } ] }}'{ "data": { "createSocialPost": { "success": true, "errors": [], "socialPost": { "id": "U29jaWFsUG9zdDo0NDEyMg==", "name": "Grand Opening Announcement", "status": "INPROGRESS", "type": "ANNOUNCEMENT", "sites": ["GOOGLE"], "publishDetails": [ { "site": "GOOGLE", "status": "PUBLISHING", "createdDate": "2026-06-28", "submissionError": null } ], "postLinks": [], "locationIds": [1800289] } } }}احتفظ بـ socialPost.id. يبدأ status بالقيمة INPROGRESS ويظل postLinks فارغًا إلى أن تقبل إحدى المنصات المنشور؛ وتسترجع الخطوة 5 كليهما.
2. نشر فعالية
تضيف الفعالية postContextInfo الذي يتضمن title وstartDay وendDay وأوقات عرض اختيارية. يرفض Google أي فعالية بلا عنوان، وتُطلق حزم SDK الاستثناء ValidationError قبل الإرسال إذا كان العنوان مفقودًا.
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "Summer Sale Kickoff Event", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="], "postType": "EVENT", "postSites": ["GOOGLE"], "postContextInfo": { "title": "Summer Sale Kickoff", "startDay": "2026-07-15", "startTime": "10:00am", "endDay": "2026-07-15", "endTime": "06:00pm" }, "postMessage": [ { "site": "GOOGLE", "message": "Join us for the Summer Sale kickoff: refreshments, giveaways, and up to 40% off in store." } ] }}'تأتي الاستجابة بالبنية نفسها كما في الخطوة 1، مع type: "EVENT" والتواريخ المُرجَعة ضمن socialPost.contextInfo. يحتوي postContextInfo على تواريخ الفعالية نفسها؛ ويتحكم postScheduledDates (الخطوة 4) في وقت نشر المنشور نفسه ووقت إنهاء نشره.
3. نشر عرض
يتضمن postContextInfo الخاص بالعرض كلًا من title وcouponCode وdiscount وredeemUrl وtermsConditions وفترة الصلاحية المحددة بـ startDay وendDay. أرفقه بزر GET_OFFER على Facebook أو ORDER على Google؛ فلا يوجد نوع REDEEM.
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "Summer 25% Off", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="], "postType": "OFFER", "postSites": ["GOOGLE"], "postContextInfo": { "title": "25% off storewide", "couponCode": "SUMMER25", "discount": "25%", "redeemUrl": "https://acmekitchen.example.com/summer", "termsConditions": "Valid in store only through July 6. Cannot be combined with other offers.", "startDay": "2026-06-22", "endDay": "2026-07-06" }, "postMessage": [ { "site": "GOOGLE", "message": "Celebrate with us: 25% off everything in store this week." } ] }}'تُرجع الاستجابة حقول العرض ضمن socialPost.contextInfo وتضبط scheduledStartDate وscheduledEndDate استنادًا إلى فترة الصلاحية.
4. إرسال منشور واحد إلى مواقع متعددة وفق جدول زمني
يقبل POST /bulk-posts الحقل input نفسه ويرسل حملة واحدة إلى كل معرّف في locationIds. أضف postScheduledDates مع startDatetime وendDatetime بتنسيق ISO-8601 للنشر لاحقًا وإنهاء النشر تلقائيًا؛ ويُنشأ المنشور حينها بالحالة SCHEDULED. خصّص لكل منصة رسالة خاصة بها عندما تختلف النبرة.
curl -X POST https://listingsapi.com/api/v4/bulk-posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "postName": "July Reopening Announcement", "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="], "postType": "ANNOUNCEMENT", "postSites": ["GOOGLE", "FACEBOOK"], "postMessage": [ { "site": "GOOGLE", "message": "We are back! Visit us this week for our summer menu." }, { "site": "FACEBOOK", "message": "We are back! Swing by for the new summer menu." } ], "postScheduledDates": { "startDatetime": "2026-07-10T09:00:00Z", "endDatetime": "2026-07-24T23:59:00Z" } }}'تُرجع نقطة النهاية الخاصة بالعمليات المجمّعة البنية نفسها التي يُرجعها POST /posts، لذا تستخدم الاستجابة المفتاح createSocialPost أيضًا، مع status: "SCHEDULED" وكلا المعرّفين في locationIds. عند وجود مشكلة في التحقق من الصحة، تُرجع نقطة النهاية 200 مع success: false ومصفوفة errors تحتوي على عناصر؛ وتُطلق حزم SDK الاستثناء ValidationError لهذه المشكلة. استرجع الحملة المجمّعة باستخدام GET /bulk-posts/{postId}، الذي يضيف حالة لكل موقع.
5. التحقق من حالة النشر وجمع روابط المنشورات
يُرجع GET /posts/{postId} الحملة ضمن socialPostView. استعلم منه دوريًا بعد الإنشاء حتى تتغير قيمة socialPostInfo.status عن INPROGRESS، ثم اقرأ publishDetails[] لمعرفة نتيجة كل منصة وpostLinks[] للحصول على عناوين URL للمنشورات المنشورة فعليًا. اترك فاصلًا بعشرات الثواني بين الاستعلامات؛ وتأتي استجابة 429 مصحوبة بـ Retry-After.
curl https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "socialPostView": { "socialPostInfo": { "id": "U29jaWFsUG9zdDo0NDEyMg==", "status": "SUCCESS", "views": 128, "clicks": 14, "publishDetails": [ { "site": "GOOGLE", "status": "PUBLISHED", "publishedDate": "2026-06-28", "submissionError": null } ], "postLinks": [ { "site": "GOOGLE", "link": "https://www.google.com/maps/place/?post=44122" } ] }, "socialPostAnalyticsBySite": [ { "site": "GOOGLE", "views": 128, "clicks": 14, "reactions": 3, "shares": 0, "comments": 1 } ] } }}تتراكم views وclicks وreactions وshares وcomments في السجل نفسه، مع تفصيلها حسب المنصة في socialPostAnalyticsBySite. تُرجع حزم SDK القيمة socialPostInfo مباشرةً، ولهذا تقرأ الأمثلة post.status.
6. عرض منشورات موقع وحذف أحدها
GET /locations/{locationId}/posts صفحة تستخدم الإزاحة (page، perPage بحد أقصى 50) وتعرض جميع الحملات التي تشمل الموقع، ضمن postsByLocation مع records[] وpageInfo.totalRecords. مرّر معرّف base64 دون تغيير؛ ولا تُخضعه لترميز URL. لا يوجد عامل تصفية حسب النوع، لذا استخرج العروض أو الفعاليات من type في الشيفرة الخاصة بك. تُعرض الحملات متعددة المواقع من الخطوة 4 بشكل منفصل بواسطة GET /locations/{locationId}/bulk-posts.
يزيل DELETE /posts/{postId} الحملة هنا ويحذفها من كل منصة وصلت إليها. لا يمكن التراجع عن الحذف، لذا تحقّق من المعرّف في القائمة أولًا.
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/posts?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY" curl -X DELETE https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"تُعيد عملية الحذف data.deleteSocialPost مع success: true وsocialPostId الذي حذفته. أما حذف معرّف سبق حذفه فيُعيد استجابة errors مع data: null بدلًا من ذلك، لذا يسهل اكتشاف محاولة الحذف الثانية.
7. الاستجابة لإشعارات webhooks الخاصة بالمنشورات
عند تعيين عنوان URL لـ webhook في /dashboard/webhooks، تغطي أربعة أحداث دورة حياة المنشور، وتَرِد جميعها في الحمولة القياسية. تُطلق هذه الأحداث لكل موقع، وليس لكل حملة: فنشر منشور جماعي في 20 موقعًا ينتج عنه 20 قيمة لـ post_item_id تشترك جميعها في قيمة واحدة لـ post_id.
function handle(payload: { event: string; location_id: string; data: Record<string, unknown> }) {if (!payload.event.startsWith('local_post.')) return; const key = String(payload.data.post_item_id); // one row per locationswitch (payload.event) { case 'local_post.published': markLive(key, payload.location_id, payload.data.post_external_id); break; case 'local_post.rejected': markRejected(key, payload.location_id, payload.data.rejection_reason); break; case 'local_post.deleted': markDeleted(key); break;}}عادةً ما يكون published_url غير موجود، لذا واصل الاستعلام الدوري في الخطوة 5 للحصول على postLinks إذا كنت بحاجة إلى رابط قابل للنقر. أرسل استجابة 200 خلال 5 ثوانٍ؛ إذ لا تُعاد محاولة الإرسال. يرد شرح المستقبِل والتحقّق من التوقيع في الاستجابة للأحداث باستخدام webhooks.
الخطوات التالية
- ربط Google واستيراد المواقع وربط صفحات Facebook: المتطلب المسبق لكل منصة.
- جلب تحليلات الملف التجاري: المشاهدات والإجراءات على مستوى الملف التجاري إلى جانب العدّادات الخاصة بكل منشور هنا.
- الاستجابة للأحداث باستخدام webhooks: المستقبِل الذي تصل إليه هذه الأحداث.
- المرجع: إنشاء منشور إعلان، إنشاء منشور فعالية، إنشاء منشور عرض، إنشاء منشور جماعي، الحصول على منشور، جلب منشورات موقع، حذف منشور.