Skip to main content
Available SocialAPI handles the Google Cloud OAuth client and Google Business Profile API access for you. No customer-side Google Cloud setup is required. See Platform credentials.

At a glance

Capabilities

Connecting

Response:
Redirect your user to auth_url. After they authorize, SocialAPI exchanges the code with Google and redirects the user to your redirect_uri with the connection result. See OAuth flows for the redirect parameters.
If the Google login manages more than one Business Profile, the redirect carries status=selection_required and a connection_id instead of an immediate account_id. See Google: connecting a login with multiple Business Profiles for the selection steps. This only applies to logins with more than one Profile; a single-Profile login connects directly.You choose one Profile at that point, but you are not limited to it. The login is kept, so its other Profiles can be attributed to your brands later through GET and POST /v1/platforms/google/logins/{loginId}/locations, with no second authorization.

Location details

GET /v1/platforms/google/logins/{loginId}/locations returns everything Google publishes about each location, so you do not need a second call to render it. The full schema is in the API reference:
details mirrors Google’s Location resource in full. Beyond the fields above it also carries more_hours (per-department hours such as a drive-through), service_area (where a business without a storefront operates), service_items (services offered, with prices), relationship_data (chain and parent/child locations), ad_phone, opening_date, and the capability flags described below. place_id is the standard Google Places identifier, so you can join a location to Places, Maps, and Geocoding results. new_review_uri is the short link that opens the review form for that location, useful for review-request campaigns. maps_uri is also promoted to the top level since it is the most commonly used one.
Every field inside details is omitted when Google returns no value for it, and details itself is absent when Google returns nothing beyond the name and description. A service-area business (a locksmith, a plumber) has no storefront_address and carries service_area instead, and a location can legitimately have no website or phone. Treat every field as optional rather than assuming a fixed shape.
has_voice_of_merchant tells you whether the location is in good standing. When it is false the merchant does not currently have control of the profile, and posting or replying will fail until they resolve it in Google Business Profile. When the field is absent entirely, Google told us nothing either way: the boolean flags distinguish a real false from an unknown, so check for presence before treating one as a denial. has_pending_edits and has_google_updated flag edits awaiting review and Google-suggested updates respectively. The can_* flags say which surfaces the location supports, for example can_operate_local_post for publishing and can_have_food_menus for restaurants.

Updating a location

PUT /v1/platforms/google/accounts/{id} writes the location’s public profile. Send only the fields you want to change:
A field you omit is left alone. A field you send with an empty value is cleared. That is how you remove a website or a phone number ("website_uri": ""), and it is also how you delete data by accident. Send only the fields you mean to change, and never round-trip a location read back into an update: fields the read omitted would arrive as empty and wipe what is there.
Lists replace rather than merge. regular_hours.periods, labels, additional_phones, and additional_categories each overwrite the whole list, so include every entry you want to keep. Nested objects are the exception: inside storefront_address you can send only postal_code and the rest of the address is untouched. Not writable: place_id, maps_uri, new_review_uri and the status flags are output-only in Google Business Profile, and language_code is fixed when the location is created (changing it requires deleting and re-creating the location in Google).

Validating before you write

Google has no sandbox, so every write hits a real Business Profile. Pass validate_only=true to have Google check the change and apply nothing:
The response carries "validated": true and an empty data, since nothing was written.
Edits are moderated. A 200 means Google accepted the change for review, not that it is live on Search and Maps. Read the location back to see what is live, and check details.has_pending_edits for edits still under review. Google also allows 10 edits per minute per Business Profile, and states that limit cannot be raised, so pace bulk updates accordingly.

Reading samples

List posts

Comments and Q&A

Google Business Profile does not offer a comments or Q&A API. Google discontinued the Business Profile Q&A API on November 3, 2025, with no replacement, and Google Business Profile has never had post-level comments. Reading or answering questions through the API is not available.

List reviews

Sample response:
Reviews may carry photos or videos the reviewer attached, returned in media as Google-hosted URLs. The reply_url field links to the reply page for that review in the Google Business Profile interface.

Reply to a review

Sample response:
A successful reply is not yet published. Google moderates review replies, so success: true means Google accepted your reply, not that anyone can see it. The reply goes live only once its state becomes APPROVED.
The state field reports where a reply sits in Google’s moderation: SocialAPI re-checks a pending reply for you and emits review.updated once Google settles it, with change set to reply_approved or reply_rejected, so subscribing to that webhook and branching on change is the easiest way to catch the replies that need rewriting. You can also read the review again with GET /v1/inbox/reviews/{account_id} and look at reply.state. A rejected reply also carries reply.policy_violation, passed through exactly as Google sends it:
Rejections are commonly caused by promotional language, discount codes, or links to sales pages. Rewrite the reply without them and send it again. Replies are also eventually consistent: reading a review immediately after replying may not show the new reply yet. Platforms that publish replies instantly omit state entirely, so treat a missing state as “already live”.

Publishing

For the full field reference, media constraints, and error recovery, see Publishing to Google.

Profile media

A location’s profile carries its own photos and videos: the logo, the cover image, interior and exterior shots. These are separate from the media attached to a post, and they are what customers see on the listing itself. List them with GET /v1/platforms/google/accounts/{account_id}/media:
Each item carries the same media object (url and type) that review photos and post attachments use, plus the category that says where it sits on the profile:

Adding a photo

Add one with POST /v1/platforms/google/accounts/{account_id}/media. The media array takes exactly one item, in the same {source_type, source} form the publishing endpoints accept, so a photo you already uploaded to your media library works here with no extra step:
All three source types work: category is one of COVER, PROFILE, LOGO, EXTERIOR, INTERIOR, PRODUCT, AT_WORK, FOOD_AND_DRINK, MENU, COMMON_AREA, ROOMS, TEAMS, or ADDITIONAL. PROFILE sets the avatar shown for the location, and LOGO is used as a fallback when no PROFILE photo exists.

Removing a photo

DELETE /v1/platforms/google/accounts/{account_id}/media/{media_id} removes a photo you added. media_id accepts either the bare id or the full resource name from the list response.

Photos your customers posted

Customers can add their own photos to a listing. Those live in a separate, read-only gallery, which you read by passing contributed=true:
Those items carry an attribution block naming the contributor, and Google requires you to display it exactly as given, without altering or removing it. They also cannot be deleted through the API: a delete returns 403. Use the takedown_url on the item to report one to Google instead.
Google limits a location to 10 edits per minute, and that limit cannot be raised. Adding or deleting several photos in a burst will hit it, so space out bulk changes.

Performance metrics

GET /v1/platforms/google/accounts/{account_id}/insights returns how the location performed day by day: how many people saw it on Google Search and Maps, and how many then called, asked for directions, messaged, booked, ordered, or clicked through to the website.
start_date and end_date are inclusive and formatted YYYY-MM-DD. metric takes a comma-separated list and defaults to every metric:
A day with no activity comes back as an explicit zero rather than a gap, so a series reads as flat instead of broken. Google publishes no retention window and no freshness lag for this data: a recent day may still be filling in, and an old range may simply return nothing. The available metrics are BUSINESS_IMPRESSIONS_DESKTOP_MAPS, BUSINESS_IMPRESSIONS_DESKTOP_SEARCH, BUSINESS_IMPRESSIONS_MOBILE_MAPS, BUSINESS_IMPRESSIONS_MOBILE_SEARCH, BUSINESS_CONVERSATIONS, BUSINESS_DIRECTION_REQUESTS, CALL_CLICKS, WEBSITE_CLICKS, BUSINESS_BOOKINGS, BUSINESS_FOOD_ORDERS, and BUSINESS_FOOD_MENU_CLICKS.

Search keywords

GET /v1/platforms/google/accounts/{account_id}/search-keywords returns what people searched before seeing the location, aggregated by month over an inclusive YYYY-MM range:
A keyword with is_threshold: true carries threshold instead of value. Google withholds counts that are too low to disclose and gives an upper bound in their place, so the real number is below the threshold. Charting a threshold as a count overstates the keyword.
limit is capped at 100 by Google, and cursor pages through the rest.

Push notifications

Google Business Profile has no HTTP webhooks. Events are published to a Google Cloud Pub/Sub topic instead, and SocialAPI runs that topic for you: when you connect a location, we point it at our topic automatically, consume the messages, and deliver them to your own webhook endpoints like every other platform’s events. There is nothing to set up on your side, and no Google Cloud project of your own is required. New reviews arrive as review.received, carrying the whole review (rating, text, author, attached photos) in data.review, so you do not have to call back for it. A reviewer editing their own review arrives as review.updated with change: "review_edited", which is the same event the reply-moderation outcomes use, so branch on change: Location-level changes arrive as account.updated, again with change naming the cause: customer_media_added, google_update_pending, voice_of_merchant_changed, or duplicate_location.
Push lowers latency; it does not replace polling. Google never redelivers a notification that was missed, so a gap in delivery is permanent and reading reviews on a schedule remains the backfill path. Keep your existing sync in place.

Limitations and gotchas

  • Local posts include updates, offers, and event posts published through Google Business Profile.
  • Account avatars are set at connect time. A Business Profile location has no profile-photo field of its own: the avatar comes from the location’s media gallery, so we read it once when you connect and store it on the account. Changing the PROFILE photo in Google later will not update the stored avatar until the account is reconnected. Reading the gallery through the media endpoints always returns the current photos.
  • Token refresh: Google OAuth tokens expire after 1 hour. SocialAPI handles refresh automatically. If you receive 401 with code: "platform.google.auth", reconnect the account.

Permissions

SocialAPI’s managed Google Cloud OAuth app already requests these scopes on your behalf: https://www.googleapis.com/auth/business.manage. You don’t need to set up a Google Cloud project. See Platform credentials.