Skip to content

SOPs

Google Business Profile API

API access approved 2026-04-16 (ticket 3-6942000041257), after a same-day denial on 2-3114000041055 (see The two applications).

Public-facing writeup: I Got Into the Google Business Profile API.

The Google Business Profile API is the programmatic door to the same listing a human manages through the dashboard. Four use cases drive the need for first-party access over third-party schedulers like GHL Social Planner:

  1. Close the review loop — programmatic reply to new reviews, routed through the same system that handles customer messages.
  2. Own the posting pipeline — push Local Posts from my own content engine instead of paying for or trusting a middleware scheduler.
  3. Pull performance data into dashboards — calls, direction requests, search impressions, photo views — alongside GSC and GA data, not inside Google’s UI.
  4. Update business info in bulk — hours, categories, attributes, services — without clicking through each profile.

Two tickets got filed under ojhurst@gmail.com, about two minutes apart.

TicketOutcomeReason
2-3114000041055Denied”A requestor’s email should be an owner/manager of a listing that has been verified for 60+ days.” The email on that application was attached to a listing that had been verified for less than 60 days.
3-6942000041257ApprovedSame day, different requestor email attached to an older verified listing.

The 60-day rule is the single biggest gotcha. It is not about the profile being 60+ days old — it is about the email’s role on that profile being 60+ days old. If I add a new manager today, that manager cannot apply for API access for another two months.

TODO: confirm and paste the exact GCP project id once I check the Developer Console.

Once confirmed, the API shows up under APIs & Services → Library → Google Business Profile API inside that project. Enable it there before the credentials will work.

Required OAuth scope for full functionality:

https://www.googleapis.com/auth/business.manage

This is a single broad scope that covers read and write across every endpoint in the Business Profile API family (locations, posts, media, reviews, insights, attributes, verifications, Q&A). There are no per-feature sub-scopes the way Gmail splits read vs modify.

Endpoint familyBase pathReadWrite
Accounts/v1/accountsList accounts I have access to
Locations/v1/accounts/{id}/locationsList, getCreate, update, delete
Local Posts/v4/accounts/{id}/locations/{id}/localPostsList, getCreate, update, delete
Media (photos + videos)/v4/accounts/{id}/locations/{id}/mediaList, getUpload
Reviews/v4/accounts/{id}/locations/{id}/reviewsList, getReply, delete reply
Q&A/v1/locations/{id}/questionsListUpvote, answer
Service items/v1/locations/{id} (servicesItems field)Read via locationUpdate via location PATCH
Insights/v1/locations/{id}:fetchMultiDailyMetricsTimeSeriesDaily metrics time series
Verifications/v1/locations/{id}/verificationsListStart, complete
Attributes/v1/attributesList attribute definitionsUpdate via location PATCH

Media notes: photos up to 5MB (JPG, PNG), videos up to 75MB and 30 seconds (MP4). Uploads are two-phase — start upload, then PUT bytes to the returned URL.

  • Default project quota: ~1 query/second sustained, bursts up to a few per second.
  • Insights endpoints have stricter per-day quotas than read endpoints.
  • Writes (especially Posts and review replies) are throttled harder than reads. If I am going to push bulk posts, batch them with backoff.
  • Google does not publish exact numbers in advance. Watch the Cloud Console quota page under the project once the API is enabled.
  • GCP project: TODO confirm.
  • OAuth client: create a new Desktop client under the project’s Credentials page. Download the client JSON.
  • Token storage: ~/apps/gbp-api/token.json (proposed — repo does not exist yet; create when I actually wire this up).
  • Authorize script: will follow the same pattern as ~/apps/gmail-helper/authorize.py — local loopback on port 8099, save refreshed token, refresh on every call.
  • CLI: ~/apps/gbp-api/gbp.py (proposed) with subcommands mirroring gmail-helper: locations, reviews, posts, insights.
  1. Review replies through Claude Code + CRM. New review arrives → webhook or polled pull → Claude drafts reply in the approval queue → approved replies posted via reviews.reply → logged to the CRM conversation history.
  2. Cross-post to GBP from the content engine. Same pipeline that posts to Facebook and LinkedIn gets a GBP node. Images served from the VPS, CTA buttons mapped to the right profile.
  3. GBP insights in the daily digest. ATH daily digest and Go Kart Park dashboard pull calls, direction requests, search impressions, and photo views alongside the CRM and Cloudflare numbers already in there.
  4. Bulk business info updates. When hours change for a holiday, a category is added, or a service item is launched, one script updates every relevant location at once.

When I want API access for a second business (different GCP project, different owner email):

  1. Verify eligibility first. The applicant email must be an owner or manager on the target listing for at least 60 days. Check the People tab on the profile.
  2. Create a new GCP project. Do not reuse the approved one — each business gets its own to keep quotas and credentials separate.
  3. Enable the Business Profile API in the new project’s API Library.
  4. Create an OAuth client under the new project.
  5. File the application at the Business Profile API support page. Be specific: what you are building, how many locations, whether it is first-party only or a tool others will use.
  6. Wait for the approval email. If denied, the email will name the rule that failed. Fix and reapply.