Google Business
Publish updates, events and offers to a Google Business Profile location, add a call-to-action button, post to many locations at once and edit published posts.
Posts on a Google listing show up in Search and Maps next to the business, not in a feed. They are a good place for this week's special, an event or a discount code. You publish them with the same POST /v1/posts call as every other platform, with platform: "googlebusiness" and a few Google-only settings in platformSpecificData.
#Post types
topicType |
Use it for | Needs |
|---|---|---|
STANDARD (default) |
News, updates, a new product | Nothing extra |
EVENT |
Anything with dates: an opening, a workshop, live music | An event with title and schedule |
OFFER |
Promotions and discounts | An offer; an event is optional and sets the offer's validity period |
Any type can add a callToAction button.
#Publish an update
curl -X POST https://api.pozy.ai/v1/posts \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Pumpkin and walnut loaves are back for the autumn. Baked fresh every morning from 7:30.",
"mediaItems": [{ "type": "image", "url": "https://pozy.ai/media/autumn-loaf.jpg" }],
"platforms": [{
"platform": "googlebusiness",
"accountId": "6713b0e5d1f4c69a2b8e3f75",
"platformSpecificData": {
"callToAction": { "type": "ORDER", "url": "https://example.com/order" }
}
}],
"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: "Pumpkin and walnut loaves are back for the autumn. Baked fresh every morning from 7:30.",
mediaItems: [{ type: "image", url: "https://pozy.ai/media/autumn-loaf.jpg" }],
platforms: [{
platform: "googlebusiness",
accountId: "6713b0e5d1f4c69a2b8e3f75",
platformSpecificData: { callToAction: { type: "ORDER", url: "https://example.com/order" } },
}],
publishNow: true,
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data);import os
import requests
H = {"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"}
resp = requests.post(
"https://api.pozy.ai/v1/posts",
headers=H,
json={
"content": "Pumpkin and walnut loaves are back for the autumn. Baked fresh every morning from 7:30.",
"mediaItems": [{"type": "image", "url": "https://pozy.ai/media/autumn-loaf.jpg"}],
"platforms": [{
"platform": "googlebusiness",
"accountId": "6713b0e5d1f4c69a2b8e3f75",
"platformSpecificData": {"callToAction": {"type": "ORDER", "url": "https://example.com/order"}},
}],
"publishNow": True,
},
timeout=30,
)
print(resp.status_code, resp.json())To schedule it, replace publishNow with scheduledFor and a timezone (Scheduling). Leave out mediaItems for a text-only post, though posts with an image or a button get more attention.
#Call-to-action buttons
callToAction.type |
Button |
|---|---|
LEARN_MORE |
Links to more information |
BOOK |
Booking or reservation page |
ORDER |
Online ordering |
SHOP |
Online shop |
SIGN_UP |
Registration or newsletter |
CALL |
Calls the business |
url must be a working HTTPS link. Avoid link shorteners; Google may not follow them.
#Publish an event
Set topicType: "EVENT" and an event with a title and a schedule. Dates are { year, month, day } objects and times are { hours, minutes } in 24-hour time. Times are optional. Pozy also accepts ISO 8601 strings for all four and converts them.
{
"platform": "googlebusiness",
"accountId": "6713b0e5d1f4c69a2b8e3f75",
"platformSpecificData": {
"topicType": "EVENT",
"event": {
"title": "Sourdough workshop",
"schedule": {
"startDate": { "year": 2026, "month": 11, "day": 14 },
"startTime": { "hours": 10, "minutes": 0 },
"endDate": { "year": 2026, "month": 11, "day": 14 },
"endTime": { "hours": 13, "minutes": 0 }
}
},
"callToAction": { "type": "SIGN_UP", "url": "https://example.com/workshop" }
}
}Google shows the title and dates as the heading of the post.
#Publish an offer
Set topicType: "OFFER" and an offer with any of couponCode, redeemOnlineUrl and termsConditions. All three are optional, but an offer with none of them gives people nothing to act on. Add an event to give the offer a heading and an end date.
{
"platform": "googlebusiness",
"accountId": "6713b0e5d1f4c69a2b8e3f75",
"platformSpecificData": {
"topicType": "OFFER",
"event": {
"title": "10% off all cakes",
"schedule": {
"startDate": { "year": 2026, "month": 12, "day": 1 },
"endDate": { "year": 2026, "month": 12, "day": 23 }
}
},
"offer": {
"couponCode": "XMAS10",
"redeemOnlineUrl": "https://example.com/cakes",
"termsConditions": "Orders placed by 23 December. One code per order."
}
}
}#Post to several locations
If one Google login manages several locations, you can post to all of them in one request. Repeat the platforms entry with the same accountId and a different locationId (locations/ followed by the location id from GET .../gmb-locations). An entry without locationId goes to the account's selected location.
{
"content": "Both shops are open on the holiday Monday, 8:00 to 14:00.",
"platforms": [
{ "platform": "googlebusiness", "accountId": "6713b0e5d1f4c69a2b8e3f75",
"platformSpecificData": { "locationId": "locations/4821930576104832190" } },
{ "platform": "googlebusiness", "accountId": "6713b0e5d1f4c69a2b8e3f75",
"platformSpecificData": { "locationId": "locations/5930184726391058277" } }
],
"publishNow": true
}The response has one platforms entry per location, each with its own status and link.
#Edit a published post
POST /v1/posts/{postId}/edit with platform: "googlebusiness" and the new content changes the text of a live post. There is no edit window and no limit on edits, and the post keeps its id. Only the text changes: the image, button, event and offer details stay as published. To change those, publish a new post.
curl -X POST https://api.pozy.ai/v1/posts/6718a5d0c6e9b14f7a3d8e2a/edit \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "googlebusiness",
"content": "Pumpkin and walnut loaves are back. Baked fresh every morning from 7:30, while they last."
}'If the post went to two accounts, add accountId to pick one. A post sent to several locations from one account always edits that account's first entry. A 404 means the post no longer exists on Google: it was deleted on Google, or it is an event or offer whose end date has passed.
#Limits
| Property | Limit |
|---|---|
| Text | 1,500 characters |
| Images | 1 per post |
| Video | Not supported |
| Image format | JPEG or PNG. WebP is converted for you. GIF is rejected. |
| Image size | Up to 5 MB. Larger images are compressed before sending. |
| Image dimensions | At least 400 x 300 px. 1200 x 900 px (4:3) crops best. |
languageCode |
Optional BCP 47 code. It labels the post and does not translate it. |
Image URLs must be public HTTPS links that return the file itself. Upload media to get one.
Google does not offer per-post analytics. Use location-level metrics from Platform insights instead.
#Common errors
| Error | Cause | Fix |
|---|---|---|
207 with post.status: "failed" |
The post was saved but Google rejected it. The reason is in platforms[].errorMessage. |
Fix and create a new post. A failed post is not retried. |
| "Request contains an invalid argument" on an event | topicType: "EVENT" without an event, or a schedule missing a date |
Add event.title, startDate and endDate |
| "Image too small" | Under 400 x 300 px | Use a larger image |
| "Invalid image format" | Not JPEG or PNG, or a corrupt file | Re-export as JPEG or PNG |
| Post published but not visible | Google is reviewing it, or the location is not verified | Allow up to 48 hours and check verification |
207 is a 2xx status, so fetch(...).ok is true. Check the status code and post.status before you report success.
#Endpoints
| Endpoint | What it does |
|---|---|
POST /v1/posts |
Create post |
POST /v1/posts/{postId}/edit |
Edit published post |
GET /v1/posts/{postId} |
Get post |
GET /v1/accounts/{accountId}/gmb-locations |
List Google Business Profile locations |
#Webhook events
| Event | When it fires |
|---|---|
post.published |
Post published. |
post.failed |
Post failed. |
post.partial |
Post partial. |
post.platform.published |
Post platform published. |
post.platform.failed |
Post platform failed. |
post.platform.deleted |
Post platform deleted. |