Posts
Publish announcements, events, and offers to Google and Facebook with the listingsapi-js SDK.
Post methods publish content to Google Business Profile and Facebook, then let
you monitor per-site publish status and analytics. All post methods live
directly on the ListingsAPI client instance.
Publishing is asynchronous: create calls return status: 'INPROGRESS', and
the post publishes in the background. Poll fetchPost until the status is
SUCCESS, and check publishDetails[].submissionError for per-site failures.
bulkPublish
Publish one post to Google and Facebook across many locations in a single
call. sites defaults to both. The message (or per-site map) expands to one
entry per site, location IDs are encoded automatically, and the payload is
validated client-side before any network call (an invalid payload throws
ValidationError immediately).
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(result.socialPost.id, result.socialPost.status); // INPROGRESS, publishes asyncPass an object as message for per-site copy:
await client.bulkPublish({name: 'Holiday hours',locationIds: [16808, 16809],message: { GOOGLE: 'Open until 10pm through the holidays. Book online!', FACEBOOK: 'We are staying open late for the holidays. See you soon!',},});Options
| Parameter | Type | Description |
|---|---|---|
name | string | Internal campaign name (not shown to customers). Required. |
locationIds | Array<string | number> | Locations to publish to (numeric or base64 IDs). Required. |
message | string or per-site map | Post body for every site, or { GOOGLE, FACEBOOK } map. Required. |
sites | PostSite[] | Target sites; defaults to ['GOOGLE', 'FACEBOOK']. |
postType | PostType | ANNOUNCEMENT (default), EVENT, OFFER, COVID19, or PRODUCT. |
ctaType | PostCtaType | BOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, or GET_OFFER. Requires ctaUrl. |
ctaUrl | string | Destination for the CTA button. Requires ctaType. |
mediaUrl | string | Public image URL attached on every site. |
scheduledDates | object | { startDatetime, endDatetime, removalSites } for scheduled publish/expiry. |
contextInfo | object | Event or offer details when postType needs them. |
additionalFields | object | Extra flat camelCase fields merged into the request body as-is. |
createAnnouncement
Create an ANNOUNCEMENT post: a plain message with optional CTA and media.
Takes the same options as bulkPublish minus postType and contextInfo;
here sites defaults to ['GOOGLE'].
await client.createAnnouncement({name: 'Grand Opening',locationIds: [16808],message: 'Acme Dental is now open in the Mission!',sites: ['GOOGLE'],ctaType: 'LEARN_MORE',ctaUrl: 'https://example.com/opening',});createEvent
Create an EVENT post with a title and a start/end window. title is
required (Google requires it); startDay and endDay are YYYY-MM-DD
dates, and startTime/endTime are optional display times. sites defaults
to ['GOOGLE'].
await client.createEvent({name: 'Live music',locationIds: [16808],message: 'Join us Friday for live jazz!',title: 'Jazz Night',startDay: '2026-08-01',endDay: '2026-08-01',startTime: '7:00pm',endTime: '10:00pm',});createOffer
Create an OFFER post with a coupon, discount, and terms. title is
required; all other offer fields are optional. sites defaults to
['GOOGLE'].
await client.createOffer({name: 'Summer sale',locationIds: [16808],message: '20% off whitening all week!',title: 'Summer Sale',couponCode: 'SUMMER20',discount: '20%',redeemUrl: 'https://example.com/sale',termsConditions: 'One per customer.',startDay: '2026-08-01',endDay: '2026-08-07',});Offer options
| Parameter | Type | Description |
|---|---|---|
title | string | Offer title. Required. |
couponCode | string | Code customers redeem. |
discount | string | Discount description, for example '20%'. |
redeemUrl | string | Online redemption link. |
termsConditions | string | Terms and conditions text. |
startDay | string | Offer validity start, YYYY-MM-DD. |
endDay | string | Offer validity end, YYYY-MM-DD. |
createPost
Create a post from a raw body (flat fields, not input-wrapped), sent to the
API as-is with no client-side validation. Prefer createAnnouncement,
createEvent, or createOffer, which build and validate this body for you.
await client.createPost({postName: 'Grand Opening',locationIds: ['TG9jYXRpb246MTY4MDg='],postType: 'ANNOUNCEMENT',postSites: ['GOOGLE'],postMessage: [{ site: 'GOOGLE', message: 'We are now open!' }],});fetchPost
Get a post with its content, per-site publish status, and analytics. Use it
to poll an INPROGRESS post until it publishes.
let post = await client.fetchPost('U29jaWFsUG9zdDo0NDEyMg=='); while (post.status === 'INPROGRESS') {await new Promise((resolve) => setTimeout(resolve, 5000));post = await client.fetchPost('U29jaWFsUG9zdDo0NDEyMg==');} for (const detail of post.publishDetails ?? []) {if (detail.submissionError) { console.log('failed:', detail.submissionError);}}deletePost
Delete a post and remove it from every site it was published to.
await client.deletePost('U29jaWFsUG9zdDo0NDEyMg==');fetchLocationPosts
List post campaigns targeting a location. Uses offset pagination (page and
perPage); tag defaults to 'all' because the API errors when it is
omitted.
const posts = await client.fetchLocationPosts(16808, { page: 1, perPage: 10 });Options
| Parameter | Type | Description |
|---|---|---|
tag | string | Segment to pull posts from; defaults to 'all'. |
page | number | Page number (1-based). |
perPage | number | Results per page. |
filters | object | Post filter fields (JSON-encoded automatically). |
sortFields | object | Sort object like { field: 'created_at', order: 'Descending' }. |
fetchBulkPost
Get a bulk campaign with per-location publish status and analytics.
const bulk = await client.fetchBulkPost('QnVsa1Bvc3Q6OTk=');fetchLocationBulkPosts
List bulk (multi-location) campaigns that include a location. Takes the same
options as fetchLocationPosts.
const campaigns = await client.fetchLocationBulkPosts(16808, { page: 1, perPage: 10 });Related resources
- Locations, look up location IDs
- Error handling,
ValidationErrorand platform error codes