向 Google 和 Facebook 发布帖子
发布公告、活动和优惠,安排一个帖子在多个地点定时发布,确认它已在每个站点上线,管理地点的帖子,并响应帖子 webhook。
帖子是地点的 Google Business Profile 或 Facebook 公共主页上带有日期的卡片,可以是公告、活动或优惠。调用一次即可创建帖子,发布过程在后台运行,再调用一次即可了解每个站点是否接受了该帖子。
所有 3 种帖子类型都使用 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。在 Facebook 上为其搭配 GET_OFFER 按钮,在 Google 上则搭配 ORDER 按钮;不存在 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 中的每个 id。添加包含 ISO-8601 格式的 startDatetime 和 endDatetime 的 postScheduledDates,即可稍后发布并自动下线;此时,帖子会以 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 中包含两个 id。如果验证出现问题,会返回 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 id;不要做 URL 编码。没有类型筛选器,因此请在您自己的代码中从 type 中筛选出优惠或活动。步骤 4 中的多地点营销活动由 GET /locations/{locationId}/bulk-posts 单独列出。
DELETE /posts/{postId} 会删除此处的营销活动,并将其从所有已发布到的网站上移除。此操作无法撤销,因此请先核对列表中的 id。
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。删除已不存在的 id 时,则会返回包含 data: null 的 errors 响应,因此很容易识别重复删除。
7. 处理帖子 webhook
在 /dashboard/webhooks 设置 webhook URL 后,4 种事件涵盖帖子的整个生命周期,均使用标准载荷。这些事件按地点触发,而非按营销活动触发:向 20 个地点批量发布帖子会产生 20 个 post_item_id 值,并共用 1 个 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。请在 5 秒内返回 200;系统不会重试。接收端和签名校验的说明见 通过 webhook 响应事件。
后续步骤
- 连接 Google 并导入地点 和 连接 Facebook 公共主页:各网站的前提条件。
- 获取资料分析数据:获取资料级别的浏览量和操作数据,作为此处各帖子计数的补充。
- 通过 webhook 响应事件:接收这些事件的接收端。
- 参考:创建公告帖子、创建活动帖子、创建优惠帖子、创建批量帖子、获取帖子、获取地点的帖子、删除帖子。