Connect Google Business Profile and import your locations
Mint a Google connect link, match the owner's Google listings to your locations, confirm the matches, and import listings that have no location yet.
Google Business Profile is the publisher most of the API depends on. Until a
Google account is connected, the Google Maps row in a location's premium
listings stays REQUIRING_ACTION, Google reviews are not pulled in, posts
cannot publish to GOOGLE, Google analytics come back null, and the Apple
Business Connect submission cannot start. Connect it first and the rest
follows.
The bulk flow (steps 1 to 6) connects a whole Google account once and matches every listing it owns to your locations. The single-location flow (step 7) connects one location from a button on its own page.
1. Mint the connect link and redirect the owner
The link is scoped to the account that owns the API key, so the body carries
only your two redirect URLs. Send the owner to the returned url; Google
returns them to successUrl or errorUrl when they finish.
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-google \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "successUrl": "https://app.example.com/connect/google/success", "errorUrl": "https://app.example.com/connect/google/error" }}'{ "data": { "bulkConnectLinkForGoogle": { "success": true, "url": "https://claim.verifymybiz.com/locations/redirect_oauth_bulk/google_oauth2?onboarding=false&platform=local&token=9009dc92-...", "errors": null } }}The link is valid for 24 hours. Check that url is non-null rather than
trusting success alone. If the owner's credentials later expire, mint a new
link the same way: the existing connection is refreshed, not duplicated.
2. List connected accounts after OAuth returns
When the owner lands on your successUrl, list the Google accounts. This is
an offset page: records[] plus pageInfo.totalRecords, paged with page
and perPage.
curl "https://listingsapi.com/api/v4/connected-accounts?publisher=GoogleAccount&page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "connectedAccountsInfo": { "pageInfo": { "hasNextPage": false, "totalRecords": 1 }, "records": [ { "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "email": "owner@brightsmile-dental.com", "status": "CONNECTED", "requestMatchesStatus": "MATCH_COMPLETED", "connectedLocationsCount": 0 } ] } }}Pick the record whose email is the owner who just consented and keep its
connectedAccountId. Every later call takes this UUID.
The optional status query filter uses different words (Connected,
NotConnected, ConnectionIssue); feeding a record's own status back in
returns a 400.
3. Trigger matching
Matching pulls every profile the account can reach and pairs each with one of your locations. It only produces suggestions; nothing is linked yet.
curl -X POST https://listingsapi.com/api/v4/connected-accounts/trigger-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountIds": ["4f712c17-4f95-42dd-90f4-97171a2e67b5"] } }'The response is data.connectedAccountsTriggerMatches with success and
failedIds. success: true means the job was queued; any account that could
not be queued is listed in failedIds.
4. Poll until MATCH_COMPLETED
Poll the account's details every 15 seconds with a bounded loop. The interval also keeps a Launch plan inside its 10 requests a minute.
curl https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/details \-H "Authorization: API $LISTINGSAPI_KEY"The record sits at data.connectedAccountDetails.details and has the same
fields as a list record. requestMatchesStatus is MATCH_IN_PROGRESS while
the job runs and MATCH_COMPLETED when suggestions are ready. Completed
means the matcher finished, not that anything is linked:
connectedLocationsCount stays 0 until you confirm.
5. Review and confirm suggestions
Each suggestion pairs one of your locations (locationInfo, with locationId
as its databaseId) with one Google listing (suggestedLocationInfo). Show
both sides to an operator, or auto-accept when name and city agree.
curl "https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/connection-suggestions?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "connectionSuggestionsForAccount": { "pageInfo": { "hasNextPage": false, "totalRecords": 2 }, "records": [ { "accountType": "GMB", "matchedDataDatabaseId": "0d5e306f-0da0-46e3-929f-fd3c67ccd8cc", "locationId": "73415", "locationInfo": { "name": "My Business", "city": "Lincoln", "stateIso": "NE" }, "suggestedLocationInfo": { "locationInfo": { "name": "My Business", "city": "Lincoln" } } } ] } }}A suggestion has no id. To confirm it you build a match-record id: the
base64 of <MatchRecordType>:<matchedDataDatabaseId>, with the type chosen
from accountType. For Google (GMB) the type is GmbLocationMatchedData,
so the record above encodes as
R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M=.
Facebook (FB) uses FbLocationMatchedData and Apple (APPLE) uses
AppleLocationMatchedData.
# base64 of GmbLocationMatchedData:0d5e306f-0da0-46e3-929f-fd3c67ccd8cccurl -X POST https://listingsapi.com/api/v4/connected-accounts/confirm-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "matchRecordIds": ["R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M="] } }'The response is data.confirmConnectMatches with success and failedIds.
failedIds holds the decoded matchedDataDatabaseId values that could not be
confirmed, such as a record already consumed or a location archived since
matching. An id built with the wrong type does not return a 400: the call
answers 200 with success: false and no failedIds, so check success
every time. Unsent suggestions stay untouched.
6. Import listings that have no location yet
Matching only pairs listings with locations you already have. To bring in the rest, list everything the account can see, then create a location from each listing you want.
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connected-account-listings \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "perPage": 50 }'{ "data": { "connectedAccountListings": { "pageInfo": { "hasNextPage": false, "totalRecords": 60 }, "records": [ { "id": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==", "locationName": "Trial", "address": "123 William St, NY 10038, Manhattan, US", "liveLink": null } ] } }}Pass locationInfo with a search term to narrow a large account by name,
address, phone, postal code, or store code. The id on each record is the
connectedAccountListingId the next two calls take. Neither SDK wraps
create-location-from-listing yet, so the Python and Node samples call REST
directly with the same header.
curl -X POST https://listingsapi.com/api/v4/connected-accounts/create-location-from-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==" }}'{ "data": { "createLocationFromConnectedAccountListing": { "success": true, "locationId": 88277, "errors": null } }}The location is created from the Google data (name, address, hours,
categories, phone, photos, attributes) and linked back to the listing at
once, so it starts in sync. locationId is the plain databaseId. On
failure success is false and errors carries a code such as SY81067
(the listing id did not resolve). The field is connectedAccountListingId;
listingId is rejected by the schema.
If the listing belongs to a location you already have, link it instead of creating a duplicate.
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==" }}'A failed link returns success: false with a message, for example
Listing record not found, or a note that the listing is already connected
to another location.
7. Connect a single location instead
For a "Connect Google" button on one location's page, skip matching and mint a link tied to that location. The owner lands back on the page you name and the credentials attach to that location only.
curl -X POST https://listingsapi.com/api/v4/locations/oauth_connect_url \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "site": "GOOGLE", "successUrl": "https://app.example.com/locations/1800289/connected", "errorUrl": "https://app.example.com/locations/1800289/connect-failed" }}'The response is data.createConnectUrl with success, url, and errors.
The link is single-use, so mint a fresh one per attempt. A relative or
unparseable redirect URL comes back as success: true with a null url,
so check url. A key that may not touch this location gets a 403 with
SY90003.
8. What happens next
Once a listing is linked, the Google Maps row in the location's premium
listings leaves REQUIRING_ACTION, moves through IN_PROGRESS, and settles
at SYNCED with a listingUrl. Google is added as a review source on its
own, so reviews start arriving in the interactions list, and updates you make
to the location flow to the profile.
With a webhook URL set in the dashboard you do not need to poll. Each payload
carries location_id and data.connected_account_id.
Next steps
- Create a Google Business Profile: for a location that has no Google listing to match.
- Connect Facebook Pages: the same flow for the other OAuth publisher.
- Check listing status and live links and Fetch reviews: watch the results arrive.
- SDK references: Python connected accounts and Node connected accounts.
- Reference: Connect a Google account, Trigger location matches, Confirm connection matches, Fetch listings of a connected account, Create a location from a listing, and the Connected accounts index.