사업장 1곳 또는 계정 전체의 리뷰 가져오기

사업장의 최신 리뷰를 나열하고, 답글이 없는 낮은 평점의 리뷰로 필터링하고, 커서로 페이지를 탐색하고, 계정 전체를 조회하고, 웹훅으로 알림을 받습니다.

API에서는 리뷰를 상호작용(interaction)이라고 합니다. Google, Facebook 또는 Yelp의 모든 리뷰가 사업장별로 하나의 목록에 반환되고 계정 전체에 대해서는 하나의 통합 목록에 반환되므로, 한 번의 연동으로 연결한 모든 소스를 처리할 수 있습니다.

1. 사업장 1곳의 최신 리뷰 나열하기

GET /locations/{locationId}/reviews는 data.interactions에 있는 커서 연결입니다. 결과는 최신순으로 반환되며 first의 기본값은 20이므로, 한 번의 호출로 전체 내역을 가져올 수는 없습니다. 경로에는 base64 id 또는 일반 숫자를 사용할 수 있습니다.

Latest reviews
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/reviews?first=10" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "interactions": {      "pageInfo": { "hasNextPage": true, "endCursor": "SW50ZXJhY3Rpb246MV8xNTU0..." },      "edges": [        {          "node": {            "id": "9e26d2a8-a6ed-42ea-8454-47d1c92ea841",            "source": "maps.google.com",            "content": "nice place for family together",            "authorName": "vaibhav nakate",            "rating": 5,            "date": "2019-04-02T22:11:13.036997+00:00",            "responded": false,            "responseCount": 0,            "canRespond": true,            "permalink": "https://maps.google.com/maps?cid=..."          },          "cursor": "SW50ZXJhY3Rpb246MV8xNTU0..."        }      ],      "totalCount": 425    }  }}

data.interactions.edges[].node를 읽습니다. id는 base64 Relay id가 아닌 일반 UUID이며, 답글을 작성할 때 전달하는 값입니다. totalCount는 현재 페이지가 아닌 전체 결과 집합에서 조건에 일치하는 항목의 수입니다. canRespond는 해당 소스가 API를 통한 소유자 답글을 허용하는지 나타냅니다.

2. 답글이 없는 낮은 평점의 리뷰로 필터링하기

배열 필터는 쿼리 문자열에서 JSON 문자열로 전달합니다. ratingFilters=[1,2]는 별점 1점과 2점인 리뷰만 남기고, responseStatus=["PENDING"]은 소유자 답글이 없는 리뷰만 남깁니다. 대괄호와 따옴표는 URL 인코딩해야 하므로, cURL 예제에서는 -G와 --data-urlencode를 함께 사용하여 각 값을 인코딩하고 URL에 추가합니다. 이에 해당하는 실제 쿼리 문자열은 ratingFilters=%5B1%2C2%5D&responseStatus=%5B%22PENDING%22%5D입니다.

Unanswered 1-2 stars
curl -G "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/reviews" \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'ratingFilters=[1,2]' \--data-urlencode 'responseStatus=["PENDING"]' \--data-urlencode 'sortOrder=["NEWEST_FIRST"]' \--data-urlencode 'first=50'

함께 사용할 수 있는 필터는 다음과 같습니다.

매개변수값참고 사항
siteUrls["maps.google.com"], ["facebook.com"], ["yelp.com"]계정의 리뷰 소스 목록 조회에서 가져온 호스트 이름입니다.
ratingFilters[1,2] 등결과에 포함할 별점입니다.
responseStatus["PENDING"] 또는 ["RESPONDED"]소유자 답글의 존재 여부입니다.
sortOrder["NEWEST_FIRST"], ["OLDEST_FIRST"], ["LAST_RESPONDED"]기본값은 최신순입니다.
startDate, endDateYYYY-MM-DDJSON이 아닌 일반 문자열입니다.
searchString자유 형식 텍스트리뷰 본문에 대한 전문 검색입니다.

3. 커서로 페이지 탐색하기

pageInfo.endCursor를 after로 다시 전달합니다. hasNextPage가 false이거나 페이지에서 빈 edges 배열이 반환되면 중지하며, 페이지 경계가 가장 오래된 리뷰에 도달하면 그다음에 빈 페이지가 1개 반환됩니다. 두 SDK 모두 페이지를 자동으로 읽을 수 있습니다.

Next page
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/reviews?first=50&after=SW50ZXJhY3Rpb246MV8xNTU0..." \-H "Authorization: API $LISTINGSAPI_KEY"

각 요청은 요청 빈도 제한에 포함되며, 리뷰가 425개인 사업장은 first=50일 때 9번의 호출이 필요합니다. 429가 반환되면 Retry-After에 따라 기다린 뒤 다음 페이지를 요청합니다. 예약된 동기화에서는 전체 내역을 다시 읽는 대신 startDate와 endDate로 실행 범위를 제한합니다. 요청 빈도 제한을 참조합니다.

4. 통합 목록으로 계정 전체 조회하기

GET /rollup_interactions는 키로 접근할 수 있는 모든 사업장의 동일한 노드를 data.rollupInteractions에 반환하며, 각 노드에는 locationId가 포함됩니다. 동일한 필터에 더해 tag도 받으며, 이 값의 기본값은 all입니다. 사업장 필터는 없으므로 locationId를 기준으로 직접 그룹화합니다. 두 SDK 모두 아직 이 엔드포인트를 래핑하지 않으므로, SDK 예제에서는 requests와 fetch를 사용하여 호출합니다.

Account-wide pending
curl -G "https://listingsapi.com/api/v4/rollup_interactions" \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'responseStatus=["PENDING"]' \--data-urlencode 'sortOrder=["NEWEST_FIRST"]' \--data-urlencode 'first=50'

각 노드는 1단계와 동일한 구조에 사업장의 databaseId인 숫자형 locationId가 추가됩니다. 통합 목록의 pageInfo에는 endCursor가 생략될 수 있으므로 마지막 edge의 cursor를 after로 사용하며, 두 값은 동일합니다.

5. id로 전체 세부 정보 가져오기

저장해 둔 id가 있으면 GET /reviewDetails가 페이지를 나누지 않고 최신 데이터를 반환합니다. interactionIds는 다른 필터처럼 URL 인코딩된 JSON 배열입니다. 응답은 data.interactionDetails.interactions에 있는 일반 배열이며, 각 상호작용에는 해당 responses가 포함됩니다.

Details by id
curl -G "https://listingsapi.com/api/v4/reviewDetails" \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'interactionIds=["9e26d2a8-a6ed-42ea-8454-47d1c92ea841"]'
JSON
{  "data": {    "interactionDetails": {      "interactions": [        {          "id": "9e26d2a8-a6ed-42ea-8454-47d1c92ea841",          "responded": true,          "responseCount": 1,          "responses": [            {              "id": "2090753a-ece6-4837-8336-8494ad308523",              "content": "Thank you so much for the kind words!",              "interactionStatus": "CONFIRMED"            }          ]        }      ]    }  }}

다른 계정의 id를 요청하면 interactionDetails: null 및 SY90001 오류와 함께 200이 반환되므로, 인덱스로 접근하기 전에 null을 확인합니다.

6. 폴링 대신 웹훅 이벤트 수신

웹훅 URL을 구성하면 리뷰를 수집하거나 리뷰가 변경될 때 interaction.review 이벤트가 발생합니다. 리뷰 필드는 data 바로 아래가 아니라 data.interaction 아래에 있으며, data.interaction.id는 목록에서 반환하는 것과 동일한 UUID입니다.

두 가지 주의 사항이 있습니다. 이 이벤트의 location_id는 정규화되지 않은 상태로 전달됩니다. 숫자 문자열이나 null일 수 있으므로 타입을 신뢰하지 말고 직접 변환해야 합니다. 또한 리뷰가 변경될 때마다 동일한 id가 다시 수신되므로, 이벤트를 일회성 삽입으로 처리하지 말고 리뷰를 다시 조회하라는 신호로 처리해야 합니다. 전송은 재시도 없이 1번만 시도하므로, 대비책으로 4단계의 주기적인 전체 조회를 유지해야 합니다. 설정 방법은 웹훅으로 이벤트에 대응하기에 있습니다.

다음 단계