Google Business Profile 사업장 연동
Google 연결 링크를 생성하고, 소유자의 Google 등록 정보를 사업장과 매칭하고, 매칭 결과를 확인한 뒤, 아직 대응하는 사업장이 없는 등록 정보를 가져옵니다.
Google 계정을 연결하면 Google에서 사업장의 콘텐츠를 게시하고 보고 데이터를 조회할 수 있습니다. 계정을 연결하기 전까지는 사업장의 프리미엄 등록 정보에서 Google Maps 행이 REQUIRING_ACTION 상태로 유지되고, Google 리뷰를 가져오지 않으며, GOOGLE에 게시물을 게시할 수 없고, Google 분석 데이터는 null로 반환됩니다. 계정을 연결하면 이 4가지 문제가 모두 해결됩니다.
일괄 연결 절차(1~6단계)는 Google 계정 전체를 한 번에 연결하고 해당 계정이 소유한 모든 등록 정보를 사업장과 매칭합니다. 단일 사업장 연결 절차(7단계)는 사업장을 한 번에 1개씩 연결합니다. 링크가 해당 사업장에 연결되어 있으므로 이후에 매칭 단계가 없습니다.
1. 연결 링크 생성 및 소유자 리디렉션
링크의 범위는 API 키를 소유한 계정으로 제한되므로 요청 본문에는 리디렉션 URL 2개만 포함합니다. 소유자를 반환된 url로 이동시키면, 소유자가 절차를 완료한 후 Google이 successUrl 또는 errorUrl로 돌려보냅니다.
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://connect.example.com/oauth?token=8f3c1d92-...", "errors": null } }}위의 url는 자리표시자입니다. 실제로 받은 응답에 포함된 url로 소유자를 이동시킵니다.
링크는 24시간 동안 유효합니다. success만 신뢰하지 말고 url가 null이 아닌지 확인합니다. 나중에 소유자의 자격 증명이 만료되면 같은 방식으로 새 링크를 생성합니다. 기존 연결이 갱신되며 중복 연결은 생성되지 않습니다.
2. OAuth에서 돌아온 후 연결된 계정 목록 조회
소유자가 successUrl에 도착하면 Google 계정 목록을 조회합니다. 이 응답은 오프셋 기반 페이지이며, records[]와 pageInfo.totalRecords로 구성되고 page와 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 } ] } }}email가 방금 동의한 소유자인 레코드를 선택하고 해당 레코드의 connectedAccountId를 저장합니다. 이후의 모든 호출에는 이 UUID가 필요합니다.
선택 사항인 status 쿼리 필터는 다른 용어(Connected, NotConnected, ConnectionIssue)를 사용하며, 레코드 자체의 status 값을 그대로 전달하면 400가 반환됩니다.
3. 매칭 시작
매칭은 계정이 접근할 수 있는 모든 프로필을 가져와 각 프로필을 사업장 중 하나와 짝짓습니다. 이 단계에서는 제안만 생성하며, 아직 아무것도 연결하지 않습니다.
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"] } }'응답은 data.connectedAccountsTriggerMatches이며 success와 failedIds를 포함합니다. success: true는 작업이 대기열에 추가되었음을 의미하며, 대기열에 추가하지 못한 계정은 모두 failedIds에 나열됩니다.
4. MATCH_COMPLETED가 될 때까지 폴링
반복 횟수에 제한을 둔 루프에서 15초마다 계정 상세 정보를 폴링합니다. 이 간격을 사용하면 Launch 요금제에서도 분당 요청 10회 제한을 지킬 수 있습니다.
curl https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/details \-H "Authorization: API $LISTINGSAPI_KEY"레코드는 data.connectedAccountDetails.details에 반환되며 목록의 레코드와 동일한 필드를 갖습니다. requestMatchesStatus는 작업이 실행 중일 때 MATCH_IN_PROGRESS이고, 제안이 준비되면 MATCH_COMPLETED가 됩니다. Completed는 매칭 기능의 실행이 끝났다는 의미이며, 무언가가 연결되었다는 의미는 아닙니다. 확정할 때까지 connectedLocationsCount는 0로 유지됩니다.
5. 제안 검토 및 확정
각 제안은 사업장 하나(locationInfo이며, databaseId는 locationId)와 Google 등록 정보 하나(suggestedLocationInfo)를 짝짓습니다. 운영자에게 양쪽 정보를 모두 표시하거나, 이름과 도시가 일치하면 자동으로 수락합니다.
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" } } } ] } }}제안에는 id가 없습니다. 제안을 확정하려면 매칭 레코드 id를 만들어야 합니다. accountType에서 유형을 선택한 후 <MatchRecordType>:<matchedDataDatabaseId>를 base64로 인코딩한 값입니다. Google(GMB)의 유형은 GmbLocationMatchedData이므로 위 레코드는 R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M=로 인코딩됩니다. Facebook(FB)은 FbLocationMatchedData를 사용하고 Apple(APPLE)은 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="] } }'응답은 data.confirmConnectMatches이며 success와 failedIds를 포함합니다. failedIds에는 이미 사용된 레코드나 매칭 이후 보관 처리된 사업장처럼 확정할 수 없었던 항목의 디코딩된 matchedDataDatabaseId 값이 들어 있습니다. 잘못된 유형으로 만든 id를 사용해도 400는 반환되지 않습니다. 호출은 success: false를 포함하고 failedIds는 없는 200를 반환하므로 매번 success를 확인해야 합니다. 전송하지 않은 제안은 변경되지 않습니다.
6. 아직 사업장이 없는 등록 정보 가져오기
매칭은 등록 정보를 기존 사업장과만 연결합니다. 나머지를 가져오려면 계정에서 볼 수 있는 모든 등록 정보를 조회한 다음, 원하는 각 등록 정보에서 사업장을 생성합니다.
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 } ] } }}locationInfo에 검색어를 전달하면 등록 정보가 많은 계정에서 이름, 주소, 전화번호, 우편번호 또는 매장 코드로 결과를 좁힐 수 있습니다. 각 레코드의 id이 다음 2개 호출에 전달하는 connectedAccountListingId입니다. 두 SDK 모두 아직 create-location-from-listing을 래핑한 기능을 제공하지 않으므로, Python과 Node 예제에서는 동일한 헤더로 REST를 직접 호출합니다.
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 } }}Google 데이터(이름, 주소, 영업시간, 카테고리, 전화번호, 속성)로 사업장이 생성되고 해당 등록 정보에 즉시 연결됩니다. Google 등록 정보는 첫 번째 사업장 업데이트가 해당 등록 정보에 게시될 때까지 IN_PROGRESS 상태를 유지합니다. locationId은 databaseId 자체입니다. 실패하면 success은 false이며, errors에는 SY81067(등록 정보 ID에 해당하는 등록 정보를 찾지 못함)과 같은 코드가 포함됩니다. 필드 이름은 connectedAccountListingId이며, listingId은 스키마에서 허용하지 않습니다.
등록 정보가 기존 사업장에 속한다면 중복 사업장을 생성하는 대신 해당 사업장에 연결합니다.
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==" }}'연결에 실패하면 Listing record not found과 같은 message 또는 등록 정보가 이미 다른 사업장에 연결되어 있다는 안내와 함께 success: false이 반환됩니다.
7. 대신 단일 사업장 연결하기
소유자가 연결하려는 사업장을 이미 알고 있다면 매칭을 건너뛰고 해당 사업장에 연결된 링크를 생성합니다. 성공하면 소유자는 successUrl으로 돌아오며, 자격 증명은 해당 사업장에만 연결됩니다.
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" }}'응답은 data.createConnectUrl이며 success, url, errors을 포함합니다. 링크는 1회용이므로 시도할 때마다 새로 생성합니다. 리디렉션 URL이 상대 URL이거나 파싱할 수 없으면 null url을 포함한 success: true이 반환되므로 url을 확인합니다. 이 사업장에 접근할 권한이 없는 키를 사용하면 SY90003를 포함한 403이 반환됩니다.
8. 이후 처리
등록 정보가 연결되면 사업장의 프리미엄 등록 정보에 있는 Google Maps 행은 REQUIRING_ACTION에서 IN_PROGRESS을 거쳐 SYNCED 상태가 되고, listingUrl이 함께 표시됩니다. Google이 리뷰 소스로 자동 추가되어 상호작용 목록에 리뷰가 들어오기 시작하며, 사업장에 적용한 업데이트가 프로필에 반영됩니다.
대시보드에 웹훅 URL을 설정하면 폴링할 필요가 없습니다. 각 페이로드에는 location_id과 data.connected_account_id이 포함됩니다.
다음 단계
- Google Business Profile 생성하기: 매칭할 Google 등록 정보가 없는 사업장에 사용합니다.
- Facebook 페이지 연결하기: 다른 OAuth 게시 플랫폼에도 동일한 절차를 적용합니다.
- 등록 정보 상태 및 공개 링크 확인하기와 리뷰 가져오기: 결과가 들어오는지 확인합니다.
- SDK 참조: Python 연결된 계정 및 Node 연결된 계정.
- 참조: Google 계정 연결하기, 사업장 매칭 실행하기, 연결 매칭 확인하기, 연결된 계정의 등록 정보 가져오기, 등록 정보에서 사업장 생성하기 및 연결된 계정 색인.