Posts

Publish announcements, events, and offers to Google and Facebook via client.posts.

All post methods live under client.posts. Methods that take a location_id accept numeric or base64-encoded IDs; see Location IDs.

Posts publish to two sites: GOOGLE and FACEBOOK. Post types are ANNOUNCEMENT, EVENT, OFFER, COVID19, and PRODUCT. Call-to-action types are BOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, and GET_OFFER; cta_type and cta_url must always be provided together.

Asynchronous publishing

Publishing is asynchronous. Create calls return status: INPROGRESS; poll posts.retrieve() (or posts.bulk_retrieve() for bulk campaigns) until the status is SUCCESS, and check publishDetails[].submissionError for per-site failures.

Per-site messages

Every create method accepts message as either a plain string, which publishes the same text to every site, or a dict mapping site to text:

Python
message = {
"GOOGLE": "Book your summer checkup today!",
"FACEBOOK": "Summer checkups are here. Tag a friend who needs one!",
}

When message is a dict, it must contain an entry for every site in sites; the SDK raises ValidationError before any network call if one is missing.


posts.bulk_publish

Publish one post across many locations on Google and Facebook in one call. This is the recommended way to post: sites defaults to both, the single message (or per-site dict) is expanded into one entry per site, location IDs are encoded automatically, and the payload is validated client-side before any network call.

client.posts.bulk_publish(
    *,
    name: str,
    location_ids: list[str | int],
    message: str | dict[str, str],
    sites: list[str] | None = None,
    post_type: str = "ANNOUNCEMENT",
    cta_type: str | None = None,
    cta_url: str | None = None,
    media_url: str | None = None,
    scheduled_dates: dict | None = None,
    context_info: dict | None = None,
    additional_fields: dict | None = None,
) -> APIObject
ArgumentTypeDescription
namestrInternal campaign name (not shown to customers). Required.
location_idslist[str | int]All locations to publish to (numeric or base64 IDs). Required.
messagestr | dictPost body for every site, or a per-site dict. Required.
siteslist[str]Target sites. Defaults to ["GOOGLE", "FACEBOOK"].
post_typestrANNOUNCEMENT (default), EVENT, OFFER, COVID19, or PRODUCT.
cta_typestrOptional call-to-action button; requires cta_url.
cta_urlstrDestination for the CTA button.
media_urlstrOptional public image URL attached on every site.
scheduled_datesdictOptional {"startDatetime", "endDatetime"} window.
context_infodictEvent or offer details when post_type needs them.
additional_fieldsdictExtra flat camelCase fields merged as-is.
Python
result = client.posts.bulk_publish(
name="Holiday hours",
location_ids=[16808, 16809, 16810],
message="Open until 10pm through the holidays!",
media_url="https://cdn.example.com/holiday.jpg",
cta_type="LEARN_MORE",
cta_url="https://example.com/holiday-hours",
)
print(result.socialPost.id, result.socialPost.status) # INPROGRESS, publishes async

posts.create_announcement

Create an ANNOUNCEMENT post: a plain message with optional CTA and media. sites defaults to ["GOOGLE"].

client.posts.create_announcement(
    *,
    name: str,
    location_ids: list[str | int],
    message: str | dict[str, str],
    sites: list[str] | None = None,
    cta_type: str | None = None,
    cta_url: str | None = None,
    media_url: str | None = None,
    scheduled_dates: dict | None = None,
    additional_fields: dict | None = None,
) -> APIObject
Python
result = client.posts.create_announcement(
name="Grand Opening",
location_ids=[16808],
message="Acme Dental is now open!",
sites=["GOOGLE"],
cta_type="LEARN_MORE",
cta_url="https://acmedental.example.com/opening",
)
print(result.socialPost.id)

posts.create_event

Create an EVENT post with a title and a start/end window. Google requires the event title; the SDK raises ValidationError if it is missing. start_day and end_day are dates (YYYY-MM-DD); start_time and end_time are display strings like "10:00am".

client.posts.create_event(
    *,
    name: str,
    location_ids: list[str | int],
    message: str | dict[str, str],
    title: str,
    start_day: str,
    end_day: str,
    start_time: str | None = None,
    end_time: str | None = None,
    sites: list[str] | None = None,
    cta_type: str | None = None,
    cta_url: str | None = None,
    media_url: str | None = None,
    scheduled_dates: dict | None = None,
    additional_fields: dict | None = None,
) -> APIObject
Python
result = client.posts.create_event(
name="Free checkup day",
location_ids=[16808],
message="Join us for free dental checkups!",
title="Free Checkup Day",
start_day="2026-08-01",
end_day="2026-08-01",
start_time="10:00am",
end_time="4:00pm",
cta_type="BOOK",
cta_url="https://acmedental.example.com/book",
)
print(result.socialPost.id)

posts.create_offer

Create an OFFER post with a coupon, discount, and terms. title labels the offer and is required; start_day and end_day (YYYY-MM-DD) bound its validity.

client.posts.create_offer(
    *,
    name: str,
    location_ids: list[str | int],
    message: str | dict[str, str],
    title: str,
    coupon_code: str | None = None,
    discount: str | None = None,
    redeem_url: str | None = None,
    terms_conditions: str | None = None,
    start_day: str | None = None,
    end_day: str | None = None,
    sites: list[str] | None = None,
    cta_type: str | None = None,
    cta_url: str | None = None,
    media_url: str | None = None,
    scheduled_dates: dict | None = None,
    additional_fields: dict | None = None,
) -> APIObject
Python
result = client.posts.create_offer(
name="Summer whitening sale",
location_ids=[16808],
message="20% off teeth whitening all week!",
title="Summer Whitening Sale",
coupon_code="SUMMER20",
discount="20%",
redeem_url="https://acmedental.example.com/sale",
terms_conditions="One per patient. Not valid with other offers.",
start_day="2026-08-01",
end_day="2026-08-07",
)
print(result.socialPost.id)

posts.create

Create a post from a raw body dict. Fields are flat camelCase (not wrapped in an input key), and locationIds must already be base64-encoded. Prefer the typed creates above for keyword arguments, automatic ID encoding, and client-side validation.

client.posts.create(body: dict) -> APIObject

Required: postName, locationIds (base64), postType, postSites.

Python
result = client.posts.create({
"postName": "Grand Opening",
"locationIds": ["TG9jYXRpb246MTY4MDg="],
"postType": "ANNOUNCEMENT",
"postSites": ["GOOGLE"],
"postMessage": [{"site": "GOOGLE", "message": "We are now open!"}],
})
print(result.socialPost.id)

posts.retrieve

Get a post with its content, per-site publish status, and analytics. Poll this after a create until status is SUCCESS.

client.posts.retrieve(post_id: str) -> APIObject
Python
post = client.posts.retrieve("U29jaWFsUG9zdDo0NDEyMg==")
print(post.status)
for detail in post.publishDetails:
print(detail.site, detail.submissionError)

posts.delete

Delete a post and remove it from every site it was published to.

client.posts.delete(post_id: str) -> APIObject
Python
client.posts.delete("U29jaWFsUG9zdDo0NDEyMg==")

posts.list_for_location

List post campaigns targeting a location. Offset-paginated: the result has .records plus .pageInfo with totalPages, totalRecords, and hasNextPage. tag defaults to "all" (the API errors when it is omitted).

client.posts.list_for_location(
    location_id: str | int,
    *,
    tag: str = "all",
    page: int | None = None,
    per_page: int | None = None,
    filters: dict | None = None,
    sort_fields: dict | None = None,
) -> APIObject
Python
result = client.posts.list_for_location(16808, page=1, per_page=10)
for post in result.records:
print(post.id, post.name)
print(result.pageInfo.totalRecords)

posts.bulk_retrieve

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

client.posts.bulk_retrieve(bulk_post_id: str) -> APIObject
Python
bulk = client.posts.bulk_retrieve("QnVsa1Bvc3Q6OTk=")
print(bulk.status)

posts.bulk_list_for_location

List bulk (multi-location) campaigns that include a location. Same parameters and result shape as posts.list_for_location.

client.posts.bulk_list_for_location(
    location_id: str | int,
    *,
    tag: str = "all",
    page: int | None = None,
    per_page: int | None = None,
    filters: dict | None = None,
    sort_fields: dict | None = None,
) -> APIObject
Python
result = client.posts.bulk_list_for_location(16808, page=1, per_page=10)
for campaign in result.records:
print(campaign.id, campaign.name)

See also

  • Locations: create and manage the locations you post to
  • Listings: check sync status per location
  • Analytics: Google, Bing, Facebook profile analytics