將貼文發布至 Google 和 Facebook

發布公告、活動和優惠,排程將一則貼文發布至多個地點,確認貼文已在各網站上線,管理地點的貼文,並處理貼文 webhook。

貼文是顯示在地點的 Google Business Profile 或 Facebook 粉絲專頁上的附日期卡片,類型包括公告、活動或優惠。第一次呼叫會建立貼文,發布作業會在背景執行,第二次呼叫則會告知您各網站是否已接受貼文。

這三種貼文類型皆使用 POST /posts,並將主體包在 input 中。未包裝的主體會遭到拒絕,並傳回 400。SDK 會建立包裝結構,並在傳送前驗證承載資料。

input 欄位用途
postName內部行銷活動名稱。不會顯示給顧客。
locationIdsBase64 地點 id。
postTypeANNOUNCEMENT、EVENT 或 OFFER。
postSitesGOOGLE、FACEBOOK 或兩者皆有。
postMessage[]postSites 中的每個網站各有一個 { site, message }。
postCta[]選用的 { site, type, url }。type 為 BOOK、ORDER、SHOP、LEARN_MORE、SIGN_UP 或 GET_OFFER。
postMediaUrl[]選用的 { site, url, type },其 type 為 IMAGE 或 VIDEO。
postContextInfo活動日期或優惠詳細資訊。EVENT 和 OFFER 必填。

1. 發布公告

公告包含訊息,以及選用的按鈕和圖片。

Announcement
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" }    ]  }}'
JSON
{  "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。

Event
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 相同,並在 socialPost.contextInfo 下傳回 type: "EVENT" 和日期。postContextInfo 儲存活動本身的日期;postScheduledDates(步驟 4)則控制貼文本身的發布和下架時間。

3. 發布優惠

優惠的 postContextInfo 包含 title、couponCode、discount、redeemUrl、termsConditions,以及有效期間 startDay 和 endDay。在 Facebook 上請搭配 GET_OFFER 按鈕,在 Google 上則搭配 ORDER 按鈕;沒有 REDEEM 類型。

Offer
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。加入 postScheduledDates,並提供 ISO-8601 格式的 startDatetime 和 endDatetime,即可延後發布並自動下架;此時貼文會以 SCHEDULED 狀態建立。若各網站需要不同語氣,請分別提供訊息。

Bulk post
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。

Post status
curl https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "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 }      ]    }  }}
欄位值意義
socialPostInfo.statusINPROGRESS, SCHEDULED, SUCCESS, EXPIRED整個行銷活動的狀態。SUCCESS 表示發布作業已完成,不代表每個網站都已接受貼文。
publishDetails[].statusPUBLISHING, PUBLISHED, ERROR每個網站各有一筆項目。狀態為 ERROR 時,submissionError 會說明原因。
postLinks[]{ site, link }發布後,各網站上已上線貼文的 URL。

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。

List and delete
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 後,四種事件會涵蓋貼文的生命週期,且皆使用標準承載資料。這些事件會按地點觸發,而非按行銷活動觸發:向 20 個地點大量發布貼文,會產生 20 個 post_item_id 值,並共用一個 post_id。

事件觸發時機額外的 data 欄位
local_post.created項目已排入佇列,等待提交至 Google。尚未上線。
local_post.publishedGoogle 已接受該項目,且項目已上線。post_external_id,即 Google 的資源名稱。
local_post.rejectedGoogle 已拒絕該項目,或提交失敗。rejection_reason.
local_post.deleted項目已刪除。post_external_id.
Route local_post events
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 回應事件。

後續步驟