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

TypeScript
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 async

Pass an object as message for per-site copy:

TypeScript
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

ParameterTypeDescription
namestringInternal campaign name (not shown to customers). Required.
locationIdsArray<string | number>Locations to publish to (numeric or base64 IDs). Required.
messagestring or per-site mapPost body for every site, or { GOOGLE, FACEBOOK } map. Required.
sitesPostSite[]Target sites; defaults to ['GOOGLE', 'FACEBOOK'].
postTypePostTypeANNOUNCEMENT (default), EVENT, OFFER, COVID19, or PRODUCT.
ctaTypePostCtaTypeBOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, or GET_OFFER. Requires ctaUrl.
ctaUrlstringDestination for the CTA button. Requires ctaType.
mediaUrlstringPublic image URL attached on every site.
scheduledDatesobject{ startDatetime, endDatetime, removalSites } for scheduled publish/expiry.
contextInfoobjectEvent or offer details when postType needs them.
additionalFieldsobjectExtra 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'].

TypeScript
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'].

TypeScript
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'].

TypeScript
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

ParameterTypeDescription
titlestringOffer title. Required.
couponCodestringCode customers redeem.
discountstringDiscount description, for example '20%'.
redeemUrlstringOnline redemption link.
termsConditionsstringTerms and conditions text.
startDaystringOffer validity start, YYYY-MM-DD.
endDaystringOffer 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.

TypeScript
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.

TypeScript
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.

TypeScript
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.

TypeScript
const posts = await client.fetchLocationPosts(16808, { page: 1, perPage: 10 });

Options

ParameterTypeDescription
tagstringSegment to pull posts from; defaults to 'all'.
pagenumberPage number (1-based).
perPagenumberResults per page.
filtersobjectPost filter fields (JSON-encoded automatically).
sortFieldsobjectSort object like { field: 'created_at', order: 'Descending' }.

fetchBulkPost

Get a bulk campaign with per-location publish status and analytics.

TypeScript
const bulk = await client.fetchBulkPost('QnVsa1Bvc3Q6OTk=');

fetchLocationBulkPosts

List bulk (multi-location) campaigns that include a location. Takes the same options as fetchLocationPosts.

TypeScript
const campaigns = await client.fetchLocationBulkPosts(16808, { page: 1, perPage: 10 });