Table of Contents
- Beyond the Dashboard Why You Need the GBP API
- What the API changes in practice
- Why review syncing is often the first real use case
- Gaining Access and Initial Project Setup
- Set up the Google Cloud project correctly
- Fill out the access request like a reviewer will read it
- What to submit before you hit send
- Authentication Showdown OAuth 2.0 vs Service Accounts
- The practical difference
- Authentication method comparison
- How to decide without overthinking it
- What usually works best
- Pulling Google Reviews A Practical Code Walkthrough
- Start by discovering accounts and locations
- Fetch every review, not just the first page
- The fields worth storing
- Replying to a review
- Building a Real-World Review Sync Feature
- A simple schema that stays stable
- The sync loop that holds up in production
- Scheduling and display
- Navigating Errors Quotas and Rejection
- Access rejection is more common than many guides admit
- Handle quotas and transient failures like infrastructure, not exceptions
- Diagnose permission problems before you blame the endpoint

Image URL
AI summary
Title
Google Business Profile API: A Practical Developer's Guide
Date
May 13, 2026
Description
A practical developer's guide to the Google Business Profile API. Learn to enable the API, handle auth, and write code to sync Google reviews to your app.
Status
Current Column
Person
Writer
You usually reach for the google business profile api when the manual process has already failed.
A teammate is copying reviews into a spreadsheet. Someone else is updating holiday hours one location at a time. Support is fielding complaints because one profile shows the wrong phone number, another still says “open,” and your product team wants a review feed inside the app by next sprint. None of that work is hard. It just doesn't scale.
The API resolves repetitive tasks, yet it brings a new set of challenges: access, authentication, incomplete documentation, and numerous edge cases that appear only when syncing live locations. Most guides end prematurely at this stage. They demonstrate a single endpoint call while ignoring the approval process, ownership complications, pagination errors, and the technical infrastructure required for a review sync that avoids duplicating records or missing data.
This guide takes the practical route. It covers access, project setup, authentication choices, review retrieval, a production-minded sync design, and the failure modes you're likely to hit.
Beyond the Dashboard Why You Need the GBP API
If you're managing one location, the dashboard is fine. If you're managing many locations, or you're building software that depends on Google reviews as an input, the dashboard becomes a bottleneck fast.
The Google Business Profile API, originally launched as the Google My Business API in late 2015, changed local SEO operations by enabling programmatic management at scale for enterprises with 50+ locations, turning isolated dashboard edits into connected systems for listing management, updates, and governance, as described in ALM Corp's overview of GBP API management at scale.

That matters even if you're not a massive chain. The same pattern shows up in smaller teams too. Once reviews, hours, URLs, or location status need to flow into another system, manual edits become operational debt.
What the API changes in practice
The dashboard is built for people. The API is built for systems.
With the API, you can treat each location as a record with rules around identity, ownership, status, and content. A workable master record usually includes internal store ID, Google store code, full NAP details, approved landing page URL, regular hours, open or closure status, ownership flags, internal business ID, and state such as active or relocated. That structure is what makes repeatable syncs possible.
A similar lesson shows up outside software. In construction, teams don't scale by relying on verbal updates alone. They standardize records, handoffs, and responsibilities. This guide to London residential building projects is useful for that reason. Different industry, same operational truth.
Why review syncing is often the first real use case
The fastest way to justify the google business profile api is usually reviews.
Reviews are public proof, but they also become product data. Teams want them in a support dashboard, a reputation workflow, an internal analytics feed, or a testimonial pipeline. That's where a direct integration becomes more useful than another browser tab.
For teams building customer proof workflows, the API becomes the bridge between Google and the rest of the stack. If you're evaluating where that review data eventually goes, review collection and display integrations show the shape of the downstream workflow.
Gaining Access and Initial Project Setup
The hard part isn't the first API call. It's getting approved to make it.
Google treats this as a controlled API, so you need both a valid Google Cloud setup and a credible business case. The cleanest approach is to prepare everything before you submit the access request. Missing details slow you down more than the technical work does.

Set up the Google Cloud project correctly
Start in Google Cloud Console and do the setup in this order:
- Create or select a projectUse a dedicated project, not a shared sandbox. Save the Project ID and Project Number somewhere obvious because you'll need both during approval and debugging.
- Enable the Business Profile APIsTurn on the APIs your integration needs. The verified setup path includes the core Business Profile APIs and related management APIs, depending on your use case.
- Configure the OAuth consent screenChoose the correct user type, fill in app details, developer contacts, and authorized domains. If you're building for production use, don't leave the consent screen half-configured and expect the rest of the process to go smoothly.
- Create OAuth credentialsGenerate a Client ID and Client Secret. Even if you later use server-side flows, you still want your auth story clean from the start.
Fill out the access request like a reviewer will read it
This part gets treated as paperwork. It isn't. It's part of the technical design.
To gain approved access, you must submit the request through the GBP API "Get Access" flow with your business justification, location IDs, and expected usage volume. Approval typically takes 3 to 10 business days, and detailed multi-location requests have about a 70% first-time success rate, while incomplete forms are rejected 40% of the time and scope overreach can trigger re-review, according to the verified guidance summarized from Google API access process notes.
Use concrete language in the form:
- State the exact use case“Sync reviews for authorized business locations into our internal reputation dashboard” is stronger than “manage business data.”
- Name who owns the locationsIf you're acting for clients, say that clearly. If you're managing owned locations, say that clearly. Ownership ambiguity causes trouble later.
- Request only the scopes you needAsking for broad permissions without a matching business reason creates friction.
What to submit before you hit send
Use this checklist:
- Project details readyProject ID, project number, linked Google accounts, and company website.
- Use case phrased in operational termsListing updates, review management, or content publishing.
- Location context includedRelevant location IDs or a clear explanation of the locations you'll manage.
- Consent screen cleaned upNo placeholder branding, no missing developer contacts, no mismatched domains.
If you skip those basics, you'll spend more time reworking access than writing code.
Authentication Showdown OAuth 2.0 vs Service Accounts
Once access is approved, your next real decision is architectural. Not “how do I authenticate?” but who is the API call acting for?
That answer determines whether you want a user-consent flow with OAuth 2.0 or a backend model for server-to-server work. Teams often blur the two and end up with brittle auth, especially when product requirements change.
The practical difference
OAuth 2.0 fits apps where a human connects an account and authorizes your application. That's the right model for an admin dashboard, an agency tool, or any product where a user expects to sign in and grant access.
Service-account style backend auth is better for scheduled jobs and internal syncs where no user is sitting at the keyboard. It keeps long-running processes predictable and moves the auth logic to infrastructure you control.
Authentication method comparison
Criterion | OAuth 2.0 (User Consent) | Service Account (Server-to-Server) |
Best fit | User-facing apps and onboarding flows | Background syncs and internal jobs |
Who grants access | End user or account owner | Your backend infrastructure |
Operational feel | More flexible for connected accounts | More stable for recurring tasks |
User interaction required | Yes | No during job execution |
Common failure mode | Broken redirect or token handling | Ownership or permission mismatch |
Good for review syncs | Yes, if customers connect their own profiles | Yes, if your system manages authorized locations centrally |
How to decide without overthinking it
Use OAuth 2.0 if these are true:
- Users connect their own Google accounts
- You need a consent screen in-product
- Your app acts on behalf of different businesses
Use a backend model if these are true:
- A scheduled worker runs the sync
- Your team controls the environment
- The same authorized account handles the locations repeatedly
There's also a security trade-off. OAuth gives users a familiar permission path, but it requires careful token storage, refresh handling, and redirect hygiene. Backend auth reduces UI complexity, but it raises the stakes on credential storage and service boundaries. If your team is reviewing that side of the design, a SaaS security overview for customer data workflows is a useful baseline for the operational questions you should ask.
What usually works best
For a customer-facing SaaS product, the most durable pattern is mixed responsibility. Use OAuth during account connection, then move the sync execution into background workers with strict token handling and clear ownership checks. That keeps the user experience clean while avoiding browser-dependent sync logic.
The bad pattern is running important review ingestion from a front-end action that can fail without notice when the tab closes.
Pulling Google Reviews A Practical Code Walkthrough
A review fetcher is typically the first feature developers build. This represents a solid choice because it validates your authentication, account discovery, location mapping, pagination, and storage model within a single process.
For review retrieval, the important detail is pagination. The reviews endpoint supports up to 100 results per page using
reviewPageToken, and skipping pagination can cause teams to miss 20% to 30% of reviews on high-volume profiles with more than 500 reviews, while observed API latency averages 200 to 500ms globally, according to the verified endpoint notes in DataForSEO's GBP API review reference.
Start by discovering accounts and locations
Use Node.js for the first pass because it's easy to wire into a product backend. The point isn't fancy abstraction. The point is getting a complete and predictable fetch.
import fetch from "node-fetch";
const ACCESS_TOKEN = process.env.GBP_ACCESS_TOKEN;
async function gbpRequest(url, options = {}) {
const res = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`,
"Content-Type": "application/json",
...(options.headers || {})
}
});
if (!res.ok) {
const text = await res.text();
throw new Error(`GBP request failed: ${res.status} ${text}`);
}
return res.json();
}
async function listAccounts() {
const url = "https://mybusiness.googleapis.com/v4/accounts";
return gbpRequest(url);
}
async function listLocations(accountName) {
const url = `https://mybusinessbusinessinformation.googleapis.com/v1/${accountName}/locations`;
return gbpRequest(url);
}
async function main() {
const accounts = await listAccounts();
console.log(JSON.stringify(accounts, null, 2));
// Pick the correct account, then list locations
const accountName = accounts.accounts?.[0]?.name; // example: accounts/123456789
if (!accountName) throw new Error("No GBP accounts found");
const locations = await listLocations(accountName);
console.log(JSON.stringify(locations, null, 2));
}
main().catch(console.error);This does two things that matter early. First, it confirms your token sees the account. Second, it tells you whether the location you expect is attached to that account at all.
Fetch every review, not just the first page
Once you have the location ID, fetch reviews with a loop. Don't build around one response and promise yourself you'll add pagination later.
import fetch from "node-fetch";
const ACCESS_TOKEN = process.env.GBP_ACCESS_TOKEN;
const ACCOUNT_ID = process.env.GBP_ACCOUNT_ID;
const LOCATION_ID = process.env.GBP_LOCATION_ID;
async function gbpRequest(url, options = {}) {
const res = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`,
"Content-Type": "application/json",
...(options.headers || {})
}
});
if (!res.ok) {
const text = await res.text();
throw new Error(`GBP request failed: ${res.status} ${text}`);
}
return res.json();
}
async function listAllReviews(accountId, locationId) {
let reviews = [];
let pageToken = null;
do {
const params = new URLSearchParams();
params.set("pageSize", "100");
if (pageToken) params.set("pageToken", pageToken);
const url = `https://mybusiness.googleapis.com/v4/accounts/${accountId}/locations/${locationId}/reviews?${params.toString()}`;
const data = await gbpRequest(url);
reviews = reviews.concat(data.reviews || []);
pageToken = data.nextPageToken || null;
} while (pageToken);
return reviews;
}
async function main() {
const reviews = await listAllReviews(ACCOUNT_ID, LOCATION_ID);
const normalized = reviews.map((r) => ({
reviewId: r.reviewId,
comment: r.comment || "",
reviewer: r.reviewer?.displayName || "Anonymous",
starRating: r.starRating,
createTime: r.createTime,
reply: r.reviewReply?.comment || null,
replyTime: r.reviewReply?.updateTime || null,
}));
console.log(JSON.stringify(normalized, null, 2));
}
main().catch(console.error);The fields worth storing
The API returns more than you need for a first release. Keep the schema tight.
Store these first:
reviewIdas your external unique key
commentfor display and moderation
reviewer.displayNamefor attribution
starRatingfor filtering and UI
createTimefor ordering and incremental sync logic
reviewReplyif your product needs response visibility
If your product team wants a quick way to compare what users see in Google versus what you've synced internally, tools for working with Google review workflows can help validate the shape of your display requirements before you overbuild the importer.
Replying to a review
If your authorized use case includes responses, the write path is straightforward:
async function replyToReview(locationId, reviewId, comment) {
const url = `https://mybusiness.googleapis.com/v4/locations/${locationId}/reviews/${reviewId}/reply`;
return gbpRequest(url, {
method: "PUT",
body: JSON.stringify({ comment })
});
}The exact endpoint shape in your implementation may vary with the API version and resources you're using, so verify against the approved docs available in your project. The important operational point is simpler: keep write logic separate from read sync logic. Reads run often. Replies need extra controls, auditability, and guardrails.
Building a Real-World Review Sync Feature
A working API call isn't a feature. A sync is a feature.
The difference lies in everything surrounding the request: storage, idempotency, scheduling, and the logic for when the same review appears on the subsequent run with new reply data. A clean integration starts to feel like product infrastructure instead of a script at this stage.

A simple schema that stays stable
You don't need a giant review model on day one. You need one that won't create duplicates.
A practical table looks like this:
Field | Purpose |
id | Internal primary key |
source | "google_business_profile" |
external_review_id | Maps to reviewId |
location_id | Maps review to a GBP location |
reviewer_name | Display name |
rating | Star rating |
body | Review text |
review_created_at | Original review timestamp |
reply_body | Stored owner reply |
reply_updated_at | Reply timestamp |
raw_payload | Full API JSON for audit/debug |
Put a unique constraint on
(source, external_review_id). That gives you idempotency even when the sync runs repeatedly.The sync loop that holds up in production
A practical sync job usually follows this sequence:
- Fetch authorized locations from your mapping table.
- Pull all pages of reviews for each location.
- Upsert on
external_review_id.
- Update reply fields if Google shows a newer reply state.
- Record sync status per location.
That last step matters more than expected. When a location fails, you want one failed location, not one failed global job.
Scheduling and display
Run the sync on a schedule that matches your product promise. A cron worker, a queue consumer, or a serverless function all work if the job is idempotent and logs enough context to debug permission failures.
On the front end, don't dump raw reviews into a generic list and call it done. Normalize ratings, trim empty comments, and keep a state for “review exists but isn't approved for display yet” if your product includes moderation. If your app surfaces customer proof as a gallery or feed, feature sets for collecting and displaying testimonial content are a useful reference point for how teams expect this data to feel in the UI.
The common failure in review syncs isn't the API call. It's forgetting that a review feed is part ingestion pipeline, part content system.
Navigating Errors Quotas and Rejection
Most trouble with the google business profile api comes from three sources: access rejection, ownership mismatch, and retry behavior that isn't deliberate enough.
The first one happens before you ship anything. The other two show up in production.
Access rejection is more common than many guides admit
Existing content often skips the ugly part. Forum discussions show frequent rejections tied to the reason “not working with clients to manage business location data,” and Google's policy language limits access to “companies in good standing who work with clients.” Support-thread reporting from 2024 to 2025 also shows 70% of applicants reporting delays over 3 months or outright denials without clear feedback, as summarized in Rollout's GBP API access analysis.
That creates a real gap for solo developers, independents, and single-location operators. The technical integration may be simple enough for them. The approval model often isn't.
What usually helps:
- Tighten the use caseBe explicit about whose locations you manage and under what authorization.
- Avoid scope inflationRequesting more than your use case needs invites scrutiny.
- Align the business model statementIf your app manages client location data, say that plainly and consistently across the form, website, and consent configuration.
What doesn't usually help is resubmitting the same vague request with slightly different wording.
Handle quotas and transient failures like infrastructure, not exceptions
Even approved integrations fail if you treat rate limits as rare events.
Use exponential backoff with jitter for
429 and retryable 5xx responses. Keep retries bounded. Log the account, location, endpoint, and request intent. If you batch multiple locations, isolate retry state per location so one noisy profile doesn't stall the whole worker.A simple retry wrapper in Node.js looks like this:
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function withRetry(fn, maxAttempts = 3) {
let attempt = 0;
while (attempt < maxAttempts) {
try {
return await fn();
} catch (err) {
attempt += 1;
const message = String(err.message || "");
const retryable =
message.includes("429") ||
message.includes("500") ||
message.includes("502") ||
message.includes("503") ||
message.includes("504");
if (!retryable || attempt >= maxAttempts) {
throw err;
}
const backoff = Math.floor((Math.random() + 1) * 500 * attempt);
await sleep(backoff);
}
}
}Diagnose permission problems before you blame the endpoint
Permission issues often look like broken code when they're really data ownership problems.
Check these first:
- Account-location relationshipConfirm the location belongs to the account you're querying.
- Authorized actorMake sure the token represents the correct approved identity.
- Environment driftTeams often test with one Google account and deploy with another.
- Resource namingA malformed account or location path can look like an auth error.
For operational debugging, keep a small error taxonomy in your codebase. Separate
AUTH, PERMISSION, RATE_LIMIT, TRANSIENT, and DATA_MAPPING failures. That classification is more useful than a wall of raw error strings. If your team wants a cleaner way to surface and inspect those failures internally, an error handling reference for customer-facing software flows is a useful benchmark for what good diagnostics should feel like.The practical takeaway is simple. Build the happy path fast, but build the failure path on purpose.
If you're collecting customer proof across channels, Testimonial gives you a cleaner way to gather, organize, and publish video and text testimonials without turning your team into spreadsheet operators. It's a solid complement to a GBP review workflow when you want approved customer feedback to move from raw ingestion into something you can showcase.
