Publish a Google Business Profile Post via API
Publish an announcement, offer or event to a location's connected platforms, and handle the draft returned when a platform is not connected.
At a glance
- What this guide accomplishes
- Publish or schedule a post to a location's connected platforms and handle missing connections.
- APIs used
- post
/api/v1/postsCreate a post - get
/api/v1/posts/{id}Get a post
- post
- Reference
- Posts
- Prerequisites
- An API key with write access
- The locationId, connected to at least one platform
- Typical use cases
- Pushing offers and announcements from your own scheduler
- Publishing an event to Google Business Profile
- Queueing a post for a future date
POST /api/v1/posts publishes an announcement, offer or event to a location's connected platforms, and by
default it goes out the moment you call it. That is the first thing to get right: set scheduledFor or
draft: true when you do not want it live immediately. One constraint to know up front, event and offer types are
Google-only. The example 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",
"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 publishPost(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: no_connection | incomplete | draft | scheduled | publishing | active | partial | error
// no_connection / incomplete: saved as a draft; the post's own status is data.post.status
// data.targets: one entry per destination, each with its own status
// 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 publish_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, missingFieldsA platform with no active connection does not fail the call. The post is saved as a draft, status comes back as
no_connection, and missingPlatforms names the platforms to connect, so your UI can prompt the customer. A
missing required field works the same way with incomplete and missingFields. That is the case to handle: a
201 does not always mean the post is live. Read status, and treat no_connection as "connect Google first",
not "done".
Choose exactly where it goes
platforms publishes through the location's connection for each platform. To pick the targets yourself, call
GET /api/v1/locations/{id}/publish-targets for the location and send the connectionIds you want as connectionIds. The
list holds the location's own profiles and its client's brand-level pages. A connection that needs reconnecting is
listed with canPublish: false: it's accepted, but publishing to it fails until the owner reconnects. The post goes
to exactly those targets, and its platforms come from them. An id that isn't one of them (a Google account-level
login, an archived connection, another location's or client's) is rejected with a 422, so a wrong id fails at the
call rather than at publish time. On PATCH /api/v1/posts/{id},
connectionIds replaces the post's targets.
Both calls return targets, one entry per destination with its own status, plus an error when that
destination failed. A post can be live on Google and still need attention on Facebook, so read targets rather
than only the post's overall status.
Posts are one of six local marketing features in the SaaS guide. To see whether a post moved the needle, retrieve profile analytics after it runs.