Google Business API
Postproxy publishes local updates to a Google Business Profile location — standard posts, events, offers, and standalone gallery photos — and manages the Business Profile itself: opening hours, attributes, services, food menus, place action links, and the photo gallery. Every request targets a specific location via location_id. Customer reviews are read and replied to through the Profile Comments API, and location analytics flow through profile stats.
Every request below uses the base URL https://api.postproxy.dev and an Authorization: Bearer YOUR_API_KEY header. Replace YOUR_API_KEY and the example IDs with your own.
Profiles and profile groups
Section titled “Profiles and profile groups”Every post targets one or more profiles. A profile is one connected account — for Google Business, a connected Business Profile account that publishes to its locations (each post picks a location via placements). Reference one in a request by its id (the prof_abc123 in the examples below) or by the platform name "google_business", which selects the group’s Google Business profile (a group holds at most one profile per platform). List what’s connected with GET /api/profiles.
Profiles live in profile groups — containers that organize the accounts for one brand, client, or project. List groups with GET /api/profile_groups, and connect a new Google Business profile with the Initialize Connection endpoint.
Example: get profiles and profile groups
List the profiles you can post to — GET /api/profiles:
curl -X GET "https://api.postproxy.dev/api/profiles" \ -H "Authorization: Bearer YOUR_API_KEY"{ "data": [ { "id": "prof_abc123", "name": "My Business", "platform": "google_business", "status": "active", "profile_group_id": "grp_xyz789", "expires_at": null, "post_count": 19, "avatar_url": "https://cdn.postproxy.dev/uploads/avatar_prof_abc123.jpg" } ]}List your profile groups — GET /api/profile_groups:
curl -X GET "https://api.postproxy.dev/api/profile_groups" \ -H "Authorization: Bearer YOUR_API_KEY"{ "data": [ { "id": "grp_xyz789", "name": "Main Brand", "profiles_count": 5 }, { "id": "grp_def456", "name": "Client Project", "profiles_count": 3 } ]}At a glance
Section titled “At a glance”| Platform ID | google_business |
| Formats | standard (default), event, offer, photo |
| Character limit | 1,500 (Photo has no body) |
| Media | Optional (local posts); required (Photo). No video |
| Placements | Locations — location_id (required) |
| Comments | Reviews via Profile Comments |
| Profile management | Hours, attributes, services, food menus, action links, profile media — see Business Profile management |
| Direct messages | No |
| Post chains | No |
Publishing
Section titled “Publishing”Formats
Section titled “Formats”| Format | Description |
|---|---|
standard | Plain local post (default) |
event | Event with a title and date range |
offer | Promotion with a validity window and optional coupon |
photo | Standalone photo uploaded to the location’s gallery (no text, no CTA) |
Shared parameters (standard, event, offer)
Section titled “Shared parameters (standard, event, offer)”| Parameter | Type | Required | Description |
|---|---|---|---|
format | string | No | standard (default), event, offer, or photo |
location_id | string | Yes | Full location resource path (e.g. accounts/123/locations/456) |
language_code | string | No | BCP 47 code (e.g. en, de). Defaults to en. Metadata only |
cta_action_type | string | No | LEARN_MORE, BOOK, ORDER, SHOP, SIGN_UP, or CALL |
cta_url | string | Conditional | HTTPS URL the CTA button opens. Required for every CTA except CALL |
event — additional parameters
Section titled “event — additional parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
event_title | string | Yes | Event title shown on the card |
event_start_date | string | Yes | YYYY-MM-DD |
event_end_date | string | Yes | YYYY-MM-DD |
event_start_time | string | No | HH:MM (24-hour) |
event_end_time | string | No | HH:MM (24-hour) |
offer — additional parameters
Section titled “offer — additional parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
event_start_date | string | Yes | YYYY-MM-DD — start of validity |
event_end_date | string | Yes | YYYY-MM-DD — end of validity |
event_start_time / event_end_time | string | No | HH:MM (24-hour) |
event_title | string | No | Offer headline (defaults to “Special Offer”) |
offer_coupon_code | string | No | Promo code shown with the offer |
offer_redeem_url | string | No | URL where the offer can be redeemed |
offer_terms | string | No | Terms and conditions |
photo — parameters
Section titled “photo — parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Full location resource path |
photo uploads a single image to the location’s gallery. The post body is ignored, and CTA / language parameters do not apply.
| Format | Image max | Formats | Count | Min dimensions |
|---|---|---|---|---|
standard / event / offer | 5 MB | jpg, png | 1 (optional) | 400×300 (recommended 1200×900, 4:3) |
photo | 5 MB | jpg, png | 1 (required) | 250×250 |
- Local-post formats accept text-only posts;
photorequires an image and ignores body text. - Video is not supported by Google Business local posts or media.
# standard post with a CTAcurl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "We are now open on Sundays, 10am–4pm — come visit!" }, "profiles": ["prof_abc123"], "platforms": { "google_business": { "format": "standard", "location_id": "accounts/123456789/locations/987654321", "cta_action_type": "LEARN_MORE", "cta_url": "https://acme.example.com/hours" } } }'# eventcurl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "Live music, free coffee, prizes." }, "profiles": ["prof_abc123"], "platforms": { "google_business": { "format": "event", "location_id": "accounts/123456789/locations/987654321", "event_title": "5-Year Anniversary", "event_start_date": "2026-06-15", "event_end_date": "2026-06-15" } } }'# offer with a couponcurl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "20% off all whole-bean coffee through the end of the month." }, "profiles": ["prof_abc123"], "media": ["https://example.com/offer.jpg"], "platforms": { "google_business": { "format": "offer", "location_id": "accounts/123456789/locations/987654321", "event_start_date": "2026-06-01", "event_end_date": "2026-06-30", "offer_coupon_code": "BEANS20", "cta_action_type": "SHOP", "cta_url": "https://example.com/shop" } } }'# photo (uploads to the location gallery; body is ignored)curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": {}, "profiles": ["prof_abc123"], "media": ["https://example.com/storefront.jpg"], "platforms": { "google_business": { "format": "photo", "location_id": "accounts/123456789/locations/987654321" } } }'Placements
Section titled “Placements”A Google Business profile may manage multiple Google accounts and any number of locations. List them with the placements endpoint — each placement’s id is the full resource path (e.g. accounts/123456789/locations/987654321) you pass as location_id. It is always required.
# List locations (placements)curl "https://api.postproxy.dev/api/profiles/prof_abc123/placements" \ -H "Authorization: Bearer YOUR_API_KEY"Each placement carries a metadata.place_id — the location’s Google Maps Place ID — so you can cross-reference it against Google Maps / Places or build a reviews permalink without a separate lookup. It’s omitted when Google returns no Place ID for the location.
{ "data": [ { "id": "accounts/123456789/locations/987654321", "name": "Acme Coffee — Downtown", "metadata": { "place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4" } } ]}Reviews
Section titled “Reviews”Google Business reviews live on a location, not on a post, so they surface through the Profile Comments API — not the post-level Comments API. Reviews sync twice daily, at 06:00 and 18:00 UTC.
# List reviews (and your replies) for a profilecurl "https://api.postproxy.dev/api/profiles/prof_abc123/comments" \ -H "Authorization: Bearer YOUR_API_KEY"# Reply to a review — parent_id is the review's external IDcurl -X POST "https://api.postproxy.dev/api/profiles/prof_abc123/comments" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "body": "Thank you for the kind words — see you next time!", "parent_id": "accounts/123456789/locations/987654321/reviews/AbFvOq" }'A profile_comment.created webhook fires for each new review (across all locations of the account).
Business Profile management
Section titled “Business Profile management”Beyond publishing, Postproxy reads and updates the Business Profile data behind each location — opening hours, attributes, services, food menus, action links, and profile photos. This is separate from publishing local posts to that location.
Every endpoint is scoped to one connected profile and one location:
/api/profiles/:profile_id/google_business/...No extra connection or scope is needed: any Google Business profile connected to Postproxy can use these endpoints. Every request requires location_id — the location’s full Google resource path (accounts/{accountId}/locations/{locationId}), exactly the placement id from Placements. A location_id your profile can’t reach returns 404; a missing one returns 400.
| Method | Endpoint | Description |
|---|---|---|
GET | /api/profiles/:profile_id/google_business/location | Get the location resource |
PATCH | /api/profiles/:profile_id/google_business/update_location | Update location fields |
GET | /api/profiles/:profile_id/google_business/available_location_categories | List valid location categories |
PATCH | /api/profiles/:profile_id/google_business/update_hours | Update regular, special, or more hours |
GET | /api/profiles/:profile_id/google_business/attributes | Get the location’s attributes |
GET | /api/profiles/:profile_id/google_business/available_attributes | List attributes the location can set |
PATCH | /api/profiles/:profile_id/google_business/update_attributes | Update attributes |
GET | /api/profiles/:profile_id/google_business/service_list | Get the service list |
PATCH | /api/profiles/:profile_id/google_business/update_service_list | Replace the service list |
GET | /api/profiles/:profile_id/google_business/food_menus | Get the food menus |
PATCH | /api/profiles/:profile_id/google_business/update_food_menus | Replace the food menus |
GET | /api/profiles/:profile_id/google_business/place_action_links | List place action links |
POST | /api/profiles/:profile_id/google_business/create_place_action_link | Create a place action link |
PATCH | /api/profiles/:profile_id/google_business/update_place_action_link | Update a place action link |
DELETE | /api/profiles/:profile_id/google_business/delete_place_action_link | Delete a place action link |
GET | /api/profiles/:profile_id/google_business/media | List profile media |
POST | /api/profiles/:profile_id/google_business/create_media | Add a photo or video to the profile |
DELETE | /api/profiles/:profile_id/google_business/delete_media | Delete a profile media item |
Read first, then patch
Section titled “Read first, then patch”Google’s update calls are field-masked: every PATCH here takes a fields array naming exactly which parts of the resource you’re replacing, and anything named in fields but absent from the body is cleared. Nested objects are replaced wholesale, not merged.
So the reliable pattern for every write is:
GETthe resource.- Edit the object you got back.
PATCHit withfieldsnaming only what you changed.
Payloads use Google’s own shapes and camelCased keys, unchanged in both directions, so a response can be sent straight back as a request body.
Feature availability
Section titled “Feature availability”Google does not offer every capability to every profile. What a location supports depends on its primary category, its country, and location-level eligibility:
| Surface | Gate |
|---|---|
| Attributes | Valid attribute IDs depend on primary category and region — always list available attributes first |
| Service list | Requires metadata.canModifyServiceList on the location |
| Food menus | Requires metadata.canHaveFoodMenus — mostly restaurant-like categories |
| Place action links | Which placeActionType values are accepted varies by profile setup |
| Profile media | Category relevance and moderation vary by profile type |
Requests blocked by one of these gates return 422 with a message naming the flag.
Location
Section titled “Location”Get location
Section titled “Get location”GET /api/profiles/:profile_id/google_business/location
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID (see above) |
read_mask | string | No | Comma-separated Google field paths. Defaults to the full profile-oriented set (name, title, website, phones, categories, address, hours, service area, service items, labels, latlng, openInfo, profile, metadata) |
curl -X GET "https://api.postproxy.dev/api/profiles/prof_abc123/google_business/location?location_id=accounts/113344/locations/558899" \ -H "Authorization: Bearer YOUR_API_KEY"{ "name": "locations/558899", "title": "Acme Coffee", "websiteUri": "https://acme.example", "phoneNumbers": { "primaryPhone": "+1 555-0100" }, "categories": { "primaryCategory": { "name": "categories/gcid:coffee_shop", "displayName": "Coffee shop" } }, "storefrontAddress": { "regionCode": "US", "locality": "San Francisco", "addressLines": ["1 Market Street"] }, "regularHours": { "periods": [ { "openDay": "MONDAY", "openTime": { "hours": 9 }, "closeDay": "MONDAY", "closeTime": { "hours": 17 } } ] }, "profile": { "description": "Neighbourhood coffee since 2009." }, "metadata": { "canModifyServiceList": true, "canHaveFoodMenus": false, "placeId": "ChIJ..." }}Update location
Section titled “Update location”PATCH /api/profiles/:profile_id/google_business/update_location
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
fields | array | Yes | Which fields to replace |
| (field values) | — | Yes | One key per entry in fields |
Patchable fields: title, storeCode, websiteUri, phoneNumbers, categories, storefrontAddress, serviceArea, labels, latlng, openInfo, profile.
Hours and the service list live on the same Google resource but have their own endpoints below.
curl -X PATCH "https://api.postproxy.dev/api/profiles/prof_abc123/google_business/update_location" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "location_id": "accounts/113344/locations/558899", "fields": ["websiteUri", "profile"], "websiteUri": "https://acme.example/coffee", "profile": { "description": "Neighbourhood coffee since 2009. Now with a roastery." } }'Naming a field in fields without sending a value returns 400 — that would otherwise silently clear it.
List available categories
Section titled “List available categories”Category values in a categories patch are Google resource names, not display names, and they differ per region. Resolve them here.
GET /api/profiles/:profile_id/google_business/available_location_categories
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
region_code | string | Yes | CLDR region, e.g. US |
language_code | string | No | BCP-47 code for display names (default en) |
filter | string | No | Google category filter expression |
view | string | No | BASIC (default) or FULL |
page_size | integer | No | 1–100 (default 100) |
page_token | string | No | From the previous response |
PATCH /api/profiles/:profile_id/google_business/update_hours
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
fields | array | Yes | Any of regularHours, specialHours, moreHours |
regularHours | object | Conditional | Weekly schedule |
specialHours | object | Conditional | Date-specific overrides (holidays) |
moreHours | array | Conditional | Named hour sets, e.g. kitchen or pickup hours |
At least one block matching fields must be present, or the request is a 400.
Times accept either "09:00" / "09:00:00" or Google’s { "hours": 9, "minutes": 0 } object; both are normalised before the call. Days are MONDAY…SUNDAY; dates are { "year", "month", "day" }.
Read current hours through Get location — they are part of the location resource.
curl -X PATCH "https://api.postproxy.dev/api/profiles/prof_abc123/google_business/update_hours" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "location_id": "accounts/113344/locations/558899", "fields": ["regularHours"], "regularHours": { "periods": [ { "openDay": "MONDAY", "openTime": "09:00", "closeDay": "MONDAY", "closeTime": "17:30" }, { "openDay": "TUESDAY", "openTime": "09:00", "closeDay": "TUESDAY", "closeTime": "17:30" } ] } }'Attributes
Section titled “Attributes”Structured facts Google shows on the profile — payments accepted, accessibility, amenities, links.
Get attributes
Section titled “Get attributes”GET /api/profiles/:profile_id/google_business/attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
{ "name": "locations/558899/attributes", "attributes": [ { "name": "attributes/has_wheelchair_accessible_entrance", "values": [true] }, { "name": "attributes/url_menu", "uriValues": [{ "uri": "https://acme.example/menu" }] } ]}List available attributes
Section titled “List available attributes”GET /api/profiles/:profile_id/google_business/available_attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
category_name | string | No | Look up by category (categories/gcid:...) instead of by location |
region_code | string | No | CLDR region, required alongside category_name |
language_code | string | No | BCP-47 code for display names |
show_all | boolean | No | Return metadata for every attribute Google knows |
page_size | integer | No | 1–200 (default 200) |
page_token | string | No | From the previous response |
Each entry carries a valueType telling you which value shape to send back.
Update attributes
Section titled “Update attributes”PATCH /api/profiles/:profile_id/google_business/update_attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
attributes | array | Yes | Objects with a name and a type-specific value field |
attribute_mask | array | No | Which attribute names to replace. Defaults to exactly the names in attributes |
Value shapes by type:
| Type | Shape |
|---|---|
| Boolean | "values": [true] |
| Enum / multi-select | "repeatedEnumValue": { "setValues": ["TOKEN"] } |
| URL | "uriValues": [{ "uri": "https://..." }] |
attribute_mask defaults to the names you sent, so a partial update never clears attributes you left out. Widen it deliberately to remove one.
curl -X PATCH "https://api.postproxy.dev/api/profiles/prof_abc123/google_business/update_attributes" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "location_id": "accounts/113344/locations/558899", "attributes": [ { "name": "attributes/pay_credit_card", "values": [true] }, { "name": "attributes/url_menu", "uriValues": [{ "uri": "https://acme.example/menu" }] } ] }'Service list
Section titled “Service list”Get service list
Section titled “Get service list”GET /api/profiles/:profile_id/google_business/service_list
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
{ "name": "locations/558899", "serviceItems": [ { "freeFormServiceItem": { "category": "categories/gcid:coffee_shop", "label": { "displayName": "Espresso bar", "languageCode": "en" } } } ]}Update service list
Section titled “Update service list”PATCH /api/profiles/:profile_id/google_business/update_service_list
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
serviceItems | array | Yes | The complete list — this replaces what’s there |
Requires metadata.canModifyServiceList on the location; otherwise 422. Item shapes are Google’s, and which fields are accepted varies by location, so read the current list and edit those objects rather than building new ones.
Food menus
Section titled “Food menus”Get food menus
Section titled “Get food menus”GET /api/profiles/:profile_id/google_business/food_menus
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
fields | array | No | name, menus |
Update food menus
Section titled “Update food menus”PATCH /api/profiles/:profile_id/google_business/update_food_menus
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
menus | array | Yes | The complete menu list — this replaces what’s there |
Requires metadata.canHaveFoodMenus; otherwise 422. Structure is menu → sections → items, all labels carry a languageCode, and prices need currencyCode and units.
curl -X PATCH "https://api.postproxy.dev/api/profiles/prof_abc123/google_business/update_food_menus" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "location_id": "accounts/113344/locations/558899", "menus": [{ "labels": [{ "displayName": "Main menu", "languageCode": "en" }], "sections": [{ "labels": [{ "displayName": "Drinks", "languageCode": "en" }], "items": [{ "labels": [{ "displayName": "Filter coffee", "languageCode": "en" }], "attributes": { "price": { "currencyCode": "USD", "units": "3" } } }] }] }] }'Place action links
Section titled “Place action links”The action buttons on the profile — “Order online”, “Book”, “Reserve a table”.
List place action links
Section titled “List place action links”GET /api/profiles/:profile_id/google_business/place_action_links
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
page_size | integer | No | 1–100 (default 100) |
page_token | string | No | From the previous response |
{ "placeActionLinks": [{ "name": "locations/558899/placeActionLinks/12345", "uri": "https://acme.example/order", "placeActionType": "FOOD_ORDERING", "isPreferred": true, "isEditable": true, "providerType": "MERCHANT" }]}Create place action link
Section titled “Create place action link”POST /api/profiles/:profile_id/google_business/create_place_action_link
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
uri | string | Yes | Where the button goes |
place_action_type | string | Yes | See below |
is_preferred | boolean | No | Make this the preferred link for its type (one per type per location) |
place_action_type: APPOINTMENT, ONLINE_APPOINTMENT, DINING_RESERVATION, FOOD_ORDERING, FOOD_DELIVERY, FOOD_TAKEOUT, SHOP_ONLINE.
Update place action link
Section titled “Update place action link”PATCH /api/profiles/:profile_id/google_business/update_place_action_link
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
name | string | Yes | Full resource name from the list response |
fields | array | Yes | Any of uri, placeActionType, isPreferred |
uri / place_action_type / is_preferred | — | Conditional | One per entry in fields |
Delete place action link
Section titled “Delete place action link”DELETE /api/profiles/:profile_id/google_business/delete_place_action_link
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
name | string | Yes | Full resource name |
{ "success": true }A name belonging to a different location returns 400.
Profile media
Section titled “Profile media”Photos and videos on the Business Profile itself — distinct from media attached to a local post.
List media
Section titled “List media”GET /api/profiles/:profile_id/google_business/media
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
page_size | integer | No | 1–250 (default 100) |
page_token | string | No | From the previous response |
Add media
Section titled “Add media”POST /api/profiles/:profile_id/google_business/create_media
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
media_url | string | Yes | Public https URL — Google fetches the file itself |
category | string | No | Default ADDITIONAL |
media_format | string | No | PHOTO (default) or VIDEO |
description | string | No | Max 2000 characters |
category: CATEGORY_UNSPECIFIED, COVER, PROFILE, LOGO, EXTERIOR, INTERIOR, PRODUCT, AT_WORK, FOOD_AND_DRINK, MENU, COMMON_AREA, ROOMS, TEAMS, ADDITIONAL.
Google downloads the file from media_url, so it has to be publicly reachable — a signed URL that expires quickly, or a localhost address, will fail.
curl -X POST "https://api.postproxy.dev/api/profiles/prof_abc123/google_business/create_media" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "location_id": "accounts/113344/locations/558899", "media_url": "https://cdn.example.com/storefront.jpg", "category": "COVER", "description": "Our storefront on Market Street" }'Delete media
Section titled “Delete media”DELETE /api/profiles/:profile_id/google_business/delete_media
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Placement ID |
media_name | string | Yes | Full resource name from the list response |
Business Profile errors
Section titled “Business Profile errors”| Status | Meaning |
|---|---|
400 | Missing location_id, an unsupported fields entry, a fields entry with no value, a bad enum, or a name on another location |
403 | Google refused the operation for this account |
404 | The location_id isn’t reachable from this profile |
422 | Not a google_business profile, the connection needs reconnecting, Google rejected the payload, or the location isn’t eligible (canModifyServiceList / canHaveFoodMenus) |
429 | Google is rate-limiting; retry after the Retry-After header |
Direct messages
Section titled “Direct messages”Not supported.
Location performance comes through the standard profile stats endpoint, one series per location — pass the location_id as placement_id. Snapshots are trailing 30-day totals: Search and Maps impressions (summed into views), website clicks, call clicks, direction requests, conversations, bookings, and food orders. See Stats fields by network.
Google Business has no follower count, and Google exposes no per-post stats for local posts — customer engagement surfaces as reviews instead (Reviews).
Webhooks
Section titled “Webhooks”Subscribe with the Webhooks API:
curl -X POST "https://api.postproxy.dev/api/webhooks" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/webhooks/postproxy", "events": ["platform_post.published", "profile_comment.created"] }'Events relevant to Google Business:
| Event | When |
|---|---|
post.processed | A post is ready to publish |
platform_post.published | A post was published to the platform |
platform_post.failed | A post failed to publish (retries exhausted) |
platform_post.failed_waiting_for_retry | A publish attempt failed; will retry |
profile_comment.created | A new review was synced, or a reply was published. Filter on data.object.placement_id per location |
profile.connected / .disconnected | Connection state changed |
profile.stats | New profile stats snapshot |
media.failed | A media attachment failed to process |
location_idis required on every post; it is the full Business Profile resource path.- Video is not supported — local posts and the gallery accept images only.
- Some verticals (e.g. lodging) have local posts disabled by Google and will return a validation error.