Create a Google Business Profile for a location that has none

Submit a brand-new Google Business Profile for a location under a connected Google account, then follow verification through the location, its listings, and webhooks.

Matching and linking only work when a Google listing already exists. A new storefront has none, so you ask the API to create one under a connected Google account. Creation is asynchronous and Google verifies the profile on its own schedule, so most of this guide is about watching the outcome.

1. Confirm the location has no Google listing

Read the location's premium listings and find the row whose site.name is Google Maps. A location with no Google presence shows REQUIRING_ACTION with a null connectedAccountId.

Google row
curl https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/listings/premium \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "site": { "name": "Google Maps", "url": "maps.google.com" },  "syncStatus": "REQUIRING_ACTION",  "displayStatus": "Connect your account to sync the listing",  "listingUrl": null,  "connectedAccountId": null,  "verified": false}

If the row is already SYNCED, or connectedAccountId is set, the location has a Google listing and this guide does not apply. Link or update it instead.

2. Pick a folder (optional)

Google organizes listings under an account into folders, also called account groups. List the connected account's folders and pass one as folderId if the owner wants the new profile inside a specific group. Skip this step to create it at the account root.

Folders
curl https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/folders \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getFoldersUnderGoogleAccount": [      { "folderId": "accounts/104890024806530350198", "folderName": "Bright Smile Dental", "locationCount": null }    ]  }}

Use folderId verbatim; it is the accounts/{id} string, not a number. locationCount may be null when Google does not report it. The endpoint is only valid for GoogleAccount connections.

3. Create the listing

Send the location's base64 id and the account, plus the folder if you chose one. The response says the request was accepted, nothing more.

Create listing
curl -X POST https://listingsapi.com/api/v4/locations/create/gmb-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5",    "folderId": "accounts/104890024806530350198"  }}'
JSON
{  "data": {    "createGmbListingForLocation": { "clientMutationId": null, "success": true, "errors": null }  }}

success: true means creation was initiated, not that a profile is live. Google reviews every new profile before it publishes, and that can take days. When success is false, errors says why: the location is missing required fields, already has a Google listing, is archived, or the connected account is no longer active.

4. Watch the location's verification status

Every location read returns googleVerificationStatus with a status and a message. Read the location back to see where Google is in the process.

Verification status
curl -G https://listingsapi.com/api/v4/locations-by-ids \--data-urlencode 'ids=["TG9jYXRpb246MTgwMDI4OQ=="]' \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "id": "TG9jYXRpb246MTgwMDI4OQ==",  "databaseId": 1800289,  "googleVerificationStatus": { "status": "PENDING_VERIFICATION", "message": null }}
statusMeaningTerminal
NOT_CONNECTEDNo Google listing is attached yet. Expect this before step 3 lands.No
NOT_VERIFIEDListing exists, verification has not started.No
PENDING_VERIFICATIONGoogle is verifying the profile.No
UNDER_REVIEWGoogle is reviewing the profile.No
VERIFIEDThe profile is live.Yes
SUSPENDEDGoogle suspended the profile.Yes
DUPLICATEGoogle judged it a duplicate of an existing profile.Yes
ERROR_WHILE_FETCHINGThe status could not be read from Google. Retry later.Yes

A daily read is enough for a process measured in days. Honor Retry-After on a 429 if you check many locations at once.

5. Watch the Google row in the listings

The premium listings row from step 1 tracks the same journey from the listing side. Re-run the step 1 call and read syncStatus, verified, and listingUrl.

syncStatusMeaning
REQUIRING_ACTIONNothing created yet, or the account needs attention. See displayStatus.
IN_PROGRESSSubmitted to Google, awaiting a response.
PENDING_APPROVALAwaiting Google's approval.
SYNCEDLive. listingUrl is populated and connectedAccountId names the account.
FAILEDGoogle rejected the submission. syncIssue says why.

6. Listen for the verification webhooks

If a webhook URL is set in the dashboard, two events cover the terminal states, so you do not have to poll. Both carry location_id and always have data.connected_account_id: null.

EventFires whendata fields
connection.google_verification_verifiedThe profile reaches VERIFIED.platform: "google"
connection.google_verification_failedThe profile reaches SUSPENDED or DUPLICATE (failure_type: "rejected") or ERROR_WHILE_FETCHING (failure_type: "transient").platform, failure_type, reason
JSON
{  "event": "connection.google_verification_failed",  "timestamp": "2026-08-25T14:52:31Z",  "account_id": 11073,  "location_id": "279381",  "data": { "platform": "google", "connected_account_id": null, "failure_type": "rejected", "reason": "This profile is suspended." }}

Only transient failures are worth retrying. The in-progress states emit no event at all, so silence is not a signal: while a verification is in flight, read googleVerificationStatus as in step 4.

Next steps