Este guia está disponível em inglês.
How to Publish Google Business Profile Posts Programmatically
Publish announcements, offers and events to Google Business Profile via API, schedule them, handle the missing-connection draft, and measure post performance.
Em resumo
- O que este guia realiza
- Create, schedule, update and measure Google Business Profile posts across a location's connected platforms.
- APIs utilizadas
- post
/api/v1/postsCriar uma publicação - get
/api/v1/postsListar publicações - get
/api/v1/posts/{id}Obter uma publicação - patch
/api/v1/posts/{id}Atualizar uma publicação - delete
/api/v1/posts/{id}Remover e excluir uma publicação - post
/api/v1/posts/{id}/publishPublicar uma publicação agora - get
/api/v1/posts/analyticsObter análises de publicações - get
/api/v1/post-ideasListar ideias de publicação
- post
- Referência
- Posts
- Pré-requisitos
- An API key with write access
- A location connected to at least one platform
- The clientId and locationId you stored at onboarding
- Casos de uso típicos
- Pushing offers and announcements from your own scheduler
- Publishing events to Google Business Profile at scale
- Reporting post performance back to a business owner
Posting is one call to publish and one to measure, with a lifecycle in between that trips people up in exactly two places: a post can go out now or be scheduled, and a post to a platform that is not connected does not fail, it comes back as a draft. Get those two right and the rest is create, list, update, delete.
How a post moves
- ComposeAnnouncement, offer or event, per platform message and a CTA
- Create
POST /api/v1/postsPublishes now, or schedules, or saves a draft - PublishLive on each connected platform; a missing connection stays a draft
- Measure
GET /api/v1/posts/analyticsViews, reactions, shares, comments over time
Post types, and which are Google-only
There are three postType values, and they are not equal across platforms:
announcement: a plain update. Works on the platforms that accept posts.offer: a promotion with a title, a window and a coupon. Google-only.event: a dated event with a start and end. Google-only.
Send an offer or event with platforms that includes only Google, or the extra fields have nowhere to go on the
others.
Publish or schedule
POST /api/v1/posts publishes immediately unless you set scheduledFor, or draft: true to hold it. Each
platform gets its own message field, so messageGoogle is Google's copy. The call below schedules a September
offer to Google.
curl -X POST https://ai.synup.com/api/v1/posts \
-H "Authorization: Bearer $SYNUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"locationId": "LOCATION_ID",
"name": "September whitening offer",
"postType": "offer",
"platforms": ["google"],
"messageGoogle": "20% off teeth whitening this September. Book online.",
"ctaType": "book",
"ctaUrl": "https://grovestreetdental.example/book",
"eventTitle": "September whitening offer",
"eventStartAt": "2026-09-01T09:00:00Z",
"eventEndAt": "2026-09-30T17:00:00Z",
"scheduledFor": "2026-09-01T09:00:00Z"
}'const API = "https://ai.synup.com/api/v1";
const headers = {
Authorization: `Bearer ${process.env.SYNUP_API_KEY}`,
"Content-Type": "application/json",
};
async function createPost(locationId, post) {
const res = await fetch(`${API}/posts`, {
method: "POST",
headers,
body: JSON.stringify({ locationId, ...post }),
});
if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
const { data } = await res.json();
// data.status: draft | scheduled | active | error
// data.missingPlatforms: platforms with no connection; data.missingFields: what a draft still needs
return data;
}import os
import requests
API = "https://ai.synup.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SYNUP_API_KEY']}", "Content-Type": "application/json"}
def create_post(location_id, post):
r = requests.post(f"{API}/posts", headers=HEADERS, timeout=30,
json={"locationId": location_id, **post})
r.raise_for_status()
return r.json()["data"] # status, missingPlatforms, missingFieldsPublish a draft later
When a draft was held because a platform was not connected, publish it once the connection exists with
POST /api/v1/posts/{id}/publish. It pushes the existing draft live without you rebuilding the body.
curl -X POST https://ai.synup.com/api/v1/posts/POST_ID/publish \
-H "Authorization: Bearer $SYNUP_API_KEY"List, inspect, update, remove
GET /api/v1/posts returns a location's posts with a stats block of totals, and each row's own status.
GET /api/v1/posts/{id} returns one. PATCH /api/v1/posts/{id} edits a post before or after it publishes, and
DELETE /api/v1/posts/{id} removes one.
# List a location's posts
curl "https://ai.synup.com/api/v1/posts?locationId=LOCATION_ID&clientId=CLIENT_ID&limit=20" \
-H "Authorization: Bearer $SYNUP_API_KEY"
# Edit the copy of an existing post
curl -X PATCH https://ai.synup.com/api/v1/posts/POST_ID \
-H "Authorization: Bearer $SYNUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "messageGoogle": "Now 25% off through September. Book online." }'
# Remove a post
curl -X DELETE https://ai.synup.com/api/v1/posts/POST_ID \
-H "Authorization: Bearer $SYNUP_API_KEY"async function listPosts(locationId, clientId) {
const params = new URLSearchParams({ locationId, clientId, limit: "20" });
const res = await fetch(`${API}/posts?${params}`, { headers });
if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
const { data } = await res.json();
return data; // { rows, total, stats: { totalPosts, totalViews, totalClicks, totalEngagement } }
}def list_posts(location_id, client_id):
r = requests.get(f"{API}/posts", headers=HEADERS, timeout=30,
params={"locationId": location_id, "clientId": client_id, "limit": 20})
r.raise_for_status()
return r.json()["data"] # rows, total, statsMeasure post performance
GET /api/v1/posts/analytics aggregates a location's posts into a summary (totalPosts, totalViews,
totalReactions, totalShares, totalComments, periodDays) with a prevSummary for the period comparison, a
byPlatform breakdown, and trend, engagementTrend and postsTrend series for charts.
curl "https://ai.synup.com/api/v1/posts/analytics?locationId=LOCATION_ID&clientId=CLIENT_ID" \
-H "Authorization: Bearer $SYNUP_API_KEY"A location with no posts yet returns the same shape with zeros, not an error, so you can render the panel before the first post goes out.
Running out of things to post
GET /api/v1/post-ideas returns AI-generated post ideas for a location, drawn from the business and the
season, which is a useful prompt library when a customer stares at an empty composer. You still choose and publish;
the ideas are suggestions, not scheduled posts.
Next
Posts are one channel. The numbers they move show up in the profile. Retrieve Google Business Profile analytics covers views, calls and direction requests. If you want a single fast recipe rather than the whole surface, the publish a post recipe is the one call. Posts also sit inside the six-feature local marketing guide.