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:
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
| Argument | Type | Description |
|---|---|---|
name | str | Internal campaign name (not shown to customers). Required. |
location_ids | list[str | int] | All locations to publish to (numeric or base64 IDs). Required. |
message | str | dict | Post body for every site, or a per-site dict. Required. |
sites | list[str] | Target sites. Defaults to ["GOOGLE", "FACEBOOK"]. |
post_type | str | ANNOUNCEMENT (default), EVENT, OFFER, COVID19, or PRODUCT. |
cta_type | str | Optional call-to-action button; requires cta_url. |
cta_url | str | Destination for the CTA button. |
media_url | str | Optional public image URL attached on every site. |
scheduled_dates | dict | Optional {"startDatetime", "endDatetime"} window. |
context_info | dict | Event or offer details when post_type needs them. |
additional_fields | dict | Extra flat camelCase fields merged as-is. |
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 asyncposts.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
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
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
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.
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
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
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
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
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
result = client.posts.bulk_list_for_location(16808, page=1, per_page=10)for campaign in result.records: print(campaign.id, campaign.name)