Platforms
Publish updates, events and offers to a Google Business Profile location, reply to reviews and read location performance and search keywords.
A Google Business Profile post shows up in Google Search, Google Maps and the business's knowledge panel, not in a feed. Posts help local ranking and fade after about a week, so a weekly rhythm works best. In Pozy the platform id is googlebusiness, and one connected account is one location.
This page covers publishing, reviews and analytics. Listing management (hours, photos, services, menus, attributes, verification) lives in the Google Business section.
#What you can do
| Feature | Supported | Where |
|---|---|---|
| Standard updates (text, text with image) | Yes | Post types |
| Call-to-action buttons | Yes | Call to action |
| Event posts | Yes | Events and offers |
| Offer posts | Yes | Events and offers |
| Posting to several locations from one account | Yes, with locationId |
Several locations |
| Scheduling | Yes | Scheduling |
| Edit a published post | Body text only | Editing |
| Reviews | List, reply, delete a reply | Inbox |
| Location analytics | Impressions, clicks, calls, directions, bookings, search keywords | Analytics |
| Listing management | Hours, media, services, menus, attributes, verification | Google Business |
| Video posts, DMs, comments, Q&A | No | Not exposed by Google |
#Connect
The location must be verified in Google. Request a connect URL and send the user to authUrl:
curl "https://api.pozy.ai/v1/connect/googlebusiness?profileId=66a1f0c2a4b9d3e8f1a2b3c4&redirect_url=https://app.example.com/connected" \
-H "Authorization: Bearer $POZY_API_KEY"const res = await fetch("https://api.pozy.ai/v1/connect/googlebusiness?profileId=66a1f0c2a4b9d3e8f1a2b3c4&redirect_url=https:%2F%2Fapp.example.com%2Fconnected", {
headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
// Send the user's browser to data.authUrlimport os, requests
resp = requests.get(
"https://api.pozy.ai/v1/connect/googlebusiness",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
params={"profileId": "66a1f0c2a4b9d3e8f1a2b3c4", "redirect_url": "https://app.example.com/connected"},
)
print(resp.json()["authUrl"])After Google's consent screen the user picks the location to connect. Pozy hosts that picker by default. To build it yourself, pass headless=true and call List locations and Select location. Once done, the user lands on your redirect_url with connected=googlebusiness, profileId and accountId appended. See Hosted connect and Headless connect.
| Connect error | Cause | Fix |
|---|---|---|
402 on the connect URL call |
The workspace needs a payment method before another account can connect | Add a card, then call again. See Billing |
| Error listing locations | Google refused: the token is invalid or the user has no access to that Business Profile | Restart the connect flow so the user grants access again |
404 on select location |
The locationId is not one this connection manages |
Use an id from the list call of the same flow |
#Post types
A post with no topicType is a standard update. Send one image at most, and no video.
curl -X POST https://api.pozy.ai/v1/posts \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Open all weekend, 8am to 8pm. The autumn menu starts Friday.",
"mediaItems": [{ "type": "image", "url": "https://pozy.ai/media/autumn-menu.jpg" }],
"platforms": [{ "platform": "googlebusiness", "accountId": "66b2e19d8c3f5a7e9d0b1c2d" }],
"publishNow": true
}'const res = await fetch("https://api.pozy.ai/v1/posts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
content: "Open all weekend, 8am to 8pm. The autumn menu starts Friday.",
mediaItems: [{ type: "image", url: "https://pozy.ai/media/autumn-menu.jpg" }],
platforms: [{ platform: "googlebusiness", accountId: "66b2e19d8c3f5a7e9d0b1c2d" }],
publishNow: true,
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.post.platforms[0].platformPostId);resp = requests.post(
"https://api.pozy.ai/v1/posts",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
json={
"content": "Open all weekend, 8am to 8pm. The autumn menu starts Friday.",
"mediaItems": [{"type": "image", "url": "https://pozy.ai/media/autumn-menu.jpg"}],
"platforms": [{"platform": "googlebusiness", "accountId": "66b2e19d8c3f5a7e9d0b1c2d"}],
"publishNow": True,
},
)
print(resp.json()["post"]["platforms"][0]["platformPostId"])Text-only posts work, but posts with an image or a button get more visibility. To schedule instead, replace publishNow with scheduledFor and timezone.
#Call to action
callToAction adds a button under the post. url must be HTTPS.
{
"platform": "googlebusiness",
"accountId": "66b2e19d8c3f5a7e9d0b1c2d",
"platformSpecificData": {
"callToAction": { "type": "BOOK", "url": "https://example.com/book" }
}
}type |
Button |
|---|---|
LEARN_MORE |
Link to more information |
BOOK |
Booking or reservation |
ORDER |
Online ordering |
SHOP |
Online store |
SIGN_UP |
Registration |
CALL |
Phone call to the business |
#Events and offers
topicType picks the post type. EVENT needs an event with a title and a schedule. OFFER takes an offer and an optional event for the offer period.
{
"platform": "googlebusiness",
"accountId": "66b2e19d8c3f5a7e9d0b1c2d",
"platformSpecificData": {
"topicType": "EVENT",
"event": {
"title": "Grand opening weekend",
"schedule": {
"startDate": { "year": 2026, "month": 11, "day": 14 },
"startTime": { "hours": 9, "minutes": 0 },
"endDate": { "year": 2026, "month": 11, "day": 15 },
"endTime": { "hours": 17, "minutes": 0 }
}
},
"callToAction": { "type": "LEARN_MORE", "url": "https://example.com/opening" }
}
}{
"platform": "googlebusiness",
"accountId": "66b2e19d8c3f5a7e9d0b1c2d",
"platformSpecificData": {
"topicType": "OFFER",
"event": {
"title": "20% off all pastries",
"schedule": {
"startDate": { "year": 2026, "month": 11, "day": 1 },
"endDate": { "year": 2026, "month": 11, "day": 30 }
}
},
"offer": {
"couponCode": "AUTUMN20",
"redeemOnlineUrl": "https://example.com/order",
"termsConditions": "In store and online. One per customer."
}
}
}Dates are { year, month, day } and times are { hours, minutes } in 24-hour time. ISO 8601 strings are also accepted for all four and converted for you. The event title and dates show as the post heading on Google. More detail, including multi-location posting, is on Google Business posts.
#Platform fields
| Field | Type | Notes |
|---|---|---|
topicType |
STANDARD, EVENT or OFFER |
Default STANDARD |
callToAction |
{ type, url } |
Button under the post |
event |
{ title, schedule } |
Required for EVENT, optional period for OFFER |
offer |
{ couponCode, redeemOnlineUrl, termsConditions } |
All optional |
locationId |
string, locations/{id} |
Post to another location the account manages |
languageCode |
string | BCP 47 code. Metadata only, it does not translate |
#Several locations
An account that manages several locations can post to the others with locationId. Get the ids with List Google Business Profile locations. Change the account's default location with Update location. See Google Business posts for posting one update to many locations.
#Edit a published post
Edit published post with platform: "googlebusiness" replaces the body text. The image, button, event and offer details stay as published. There is no edit window and no edit limit. The id in the response is Google's post id, not the Pozy post id you put in the path.
A 404 on edit means the post no longer exists on Google: it was deleted in Google's dashboard, or it was an event or offer whose end date passed. Publish a new post instead.
#Media requirements
| Images | Requirement |
|---|---|
| Per post | 1 |
| Video | Not supported |
| Formats | JPEG, PNG. WebP is converted for you. GIF is not supported |
| Max size | 5 MB. Larger images are compressed before sending |
| Minimum | 400 x 300 px |
| Recommended | 1200 x 900 px (4:3). Google crops, and 4:3 survives the crop best |
The URL must be public HTTPS and return the file with no login or redirect. Upload through Media if you are not sure.
#Limits
| Limit | Value |
|---|---|
| Post text | 1,500 characters |
| Images | 1 per post |
| Visibility | Roughly 7 days, then Google archives the post |
| Time to appear | Posts can take 24 to 48 hours while Google reviews them |
| Analytics history | Up to 18 months back |
#Analytics
Google has retired per-post insights, so Business Profile analytics are per location.
Get performance metrics returns daily values and totals for each metric. Pass accountId and a date range (it defaults to the last 30 days), and optionally metrics as a comma-separated list. Google's data runs 2 to 3 days behind.
curl "https://api.pozy.ai/v1/analytics/googlebusiness/performance?accountId=66b2e19d8c3f5a7e9d0b1c2d&startDate=2026-09-01&endDate=2026-09-30" \
-H "Authorization: Bearer $POZY_API_KEY"const res = await fetch("https://api.pozy.ai/v1/analytics/googlebusiness/performance?accountId=66b2e19d8c3f5a7e9d0b1c2d&startDate=2026-09-01&endDate=2026-09-30", {
headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.metrics.WEBSITE_CLICKS);resp = requests.get(
"https://api.pozy.ai/v1/analytics/googlebusiness/performance",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
params={"accountId": "66b2e19d8c3f5a7e9d0b1c2d", "startDate": "2026-09-01", "endDate": "2026-09-30"},
)
print(resp.json()["metrics"]["WEBSITE_CLICKS"])| Metric | Meaning |
|---|---|
BUSINESS_IMPRESSIONS_DESKTOP_SEARCH, BUSINESS_IMPRESSIONS_MOBILE_SEARCH |
Views in Google Search |
BUSINESS_IMPRESSIONS_DESKTOP_MAPS, BUSINESS_IMPRESSIONS_MOBILE_MAPS |
Views in Google Maps |
WEBSITE_CLICKS |
Clicks to the website |
CALL_CLICKS |
Taps to call |
BUSINESS_DIRECTION_REQUESTS |
Direction requests |
BUSINESS_CONVERSATIONS |
Message conversations started |
BUSINESS_BOOKINGS |
Bookings |
BUSINESS_FOOD_ORDERS, BUSINESS_FOOD_MENU_CLICKS |
Food orders and menu clicks |
Get search keywords returns the searches that showed the listing, aggregated by month (startMonth and endMonth as YYYY-MM, default the last 3 months). Keywords below Google's privacy threshold come back with impressions: 0 and impressionsThreshold set. Each page holds up to 100 keywords; pass nextPageToken back as pageToken for more.
#Inbox
Reviews are the only inbox surface. List them with List reviews and platform=googlebusiness, filter by accountId, minRating, maxRating and hasReply, then answer with Reply to review. The review id is Google's full resource name, so it also identifies the location.
curl "https://api.pozy.ai/v1/inbox/reviews?platform=googlebusiness&accountId=66b2e19d8c3f5a7e9d0b1c2d&hasReply=false" \
-H "Authorization: Bearer $POZY_API_KEY"New, edited, removed and restored reviews arrive as webhooks. See Reviews and Google Business reviews.
#Errors you may see
| Error | Cause | Fix |
|---|---|---|
| "Image not found" | The image URL is not reachable or needs a login | Use a public HTTPS URL that loads the raw file |
| "Invalid image format" | Not JPEG or PNG, or the file is corrupted | Re-export as JPEG or PNG |
| "Image too small" | Under 400 x 300 px | Use at least 400 x 300 px, ideally 1200 x 900 px |
| Post not visible on Google | Still in Google's review, or the location is not verified | Wait up to 48 hours and check verification |
| Button does nothing | The callToAction.url is invalid or shortened |
Use a full HTTPS URL |
401 with token_invalid |
Google revoked or expired the token | Reconnect the account. See Account health |
Errors covers the error format.
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/connect/{platform} |
Get OAuth connect URL |
GET /v1/connect/googlebusiness/locations |
List Google Business Profile locations |
POST /v1/connect/googlebusiness/select-location |
Select Google Business Profile location |
GET /v1/accounts/{accountId}/gmb-locations |
List Google Business Profile locations |
PUT /v1/accounts/{accountId}/gmb-locations |
Update Google Business Profile location |
POST /v1/posts |
Create post |
POST /v1/posts/{postId}/edit |
Edit published post |
GET /v1/analytics/googlebusiness/performance |
Get Google Business Profile performance metrics |
GET /v1/analytics/googlebusiness/search-keywords |
Get Google Business Profile search keywords |
GET /v1/inbox/reviews |
List reviews |
POST /v1/inbox/reviews/{reviewId}/reply |
Reply to review |
DELETE /v1/inbox/reviews/{reviewId}/reply |
Delete review reply |
#Webhook events
| Event | When it fires |
|---|---|
post.published |
Post published. |
post.failed |
Post failed. |
post.platform.published |
Post platform published. |
post.platform.failed |
Post platform failed. |
post.platform.deleted |
Post platform deleted. |
review.new |
Review new. |
review.updated |
Review updated. |
review.deleted |
Review deleted. |
review.restored |
Review restored. |