사업장
목록 조회, 검색, id 및 매장 코드로 조회, 생성, 업데이트, 보관 처리, 사진 관리를 다루는 모든 사업장 엔드포인트를 사용 순서대로 소개하며, 각 엔드포인트에 cURL, Python 및 Node 예제를 제공합니다.
등록 정보, 리뷰, 게시물, 분석은 모두 사업장 ID를 기준으로 연결됩니다. 사업장 리소스에는 엔드포인트가 13개 있으며, 사업장을 찾는 읽기 엔드포인트 4개, 사업장을 생성, 변경, 보관 처리하는 쓰기 엔드포인트 4개, 사진을 관리하는 엔드포인트 5개로 구성됩니다. Read(읽기) 키로 읽기 작업을 수행할 수 있습니다. 모든 POST에는 Write(쓰기) 키가 필요하며, 쓰기 작업에 Read 키를 사용하면 SY90016과 함께 HTTP 400이 반환됩니다.
이 가이드의 모든 id는 base64 Relay id입니다. 예를 들어 TG9jYXRpb246MTgwMDI4OQ==를 디코딩하면 Location:1800289가 됩니다. 두 SDK 모두 숫자형 databaseId도 입력받아 자동으로 인코딩합니다.
엔드포인트 한눈에 보기
모든 사업장 조회
GET /locations는 계정 내 모든 사업장을 최신순으로 페이지 단위로 조회합니다. 커서 기반 연결 구조이며, edges[].node에는 각 레코드가 들어 있고, pageInfo.endCursor는 다음 페이지를 위한 after 매개변수에 전달됩니다. 계정 전체를 자체 시스템에 동기화할 때 사용합니다.
매개변수
curl "https://listingsapi.com/api/v4/locations?first=50" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "allLocations": { "edges": [ { "cursor": "TG9jYXRpb246MTgwMDI4OQ==", "node": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "storeId": null, "approved": "APPROVED", "archived": false, "archivalScheduledAt": null, "city": "Austin", "stateIso": "TX", "countryIso": "US", "subCategoryId": 1432, "planName": "Launch", "placeActionLinks": [], "discoveredPlaceActionLinks": [] } } ], "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "endCursor": "TG9jYXRpb246MTgwMDI4OQ==", "total": 1 } } }}pageInfo.hasNextPage를 확인하고 값이 false이면 중단합니다. 페이지 수준의 개수는 pageInfo.total입니다. 노드의 id는 다른 모든 엔드포인트에서 요구하는 식별자입니다.
사업장 검색
GET /locations/search는 사업장 이름, 도로명 주소, 도시, 매장 id에서 키워드와 일치하는 항목을 찾습니다. 목록 조회와 동일한 edges[].node 및 pageInfo.endCursor 형태의 커서 기반 연결 구조입니다. 입력 중 검색 결과를 표시하는 선택기를 구현하거나, 매장을 생성하기 전에 이미 존재하는지 확인할 때 사용합니다.
매개변수
두 SDK 모두 fields 목록도 입력받아 검색 대상을 특정 필드로 제한할 수 있으며, 예를 들어 name만 검색하도록 제한할 수 있습니다.
curl "https://listingsapi.com/api/v4/locations/search?query=Jenny&first=10" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "searchLocations": { "edges": [ { "cursor": "TG9jYXRpb246MTgwMDI4OQ==", "node": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "storeId": null, "street": "600 Congress Ave, Suite 200", "city": "Austin", "approved": "APPROVED", "archived": false } } ], "pageInfo": { "endCursor": "TG9jYXRpb246MTgwMDI4OQ==", "hasNextPage": false, "hasPreviousPage": false, "total": 1 } } }}edges 배열이 비어 있으면 일치하는 항목이 없다는 뜻입니다. 노드에는 전체 주소, 카테고리, approved 상태가 포함되므로, 결과 행을 표시하기 위해 추가로 조회할 필요가 없습니다.
ID로 사업장 조회
GET /locations-by-ids는 여러 base64 ID에 해당하는 전체 레코드를 한 번의 호출로 반환하므로, 사업장마다 개별 호출할 때보다 요청 수가 적습니다. 활성 사업장과 보관된 사업장 모두 조회할 수 있습니다. 연결(connection) 구조가 아닌 일반 배열을 반환합니다. 웹훅 페이로드나 자체 저장소에 있는 ID로 전체 레코드를 가져올 때 사용합니다.
매개변수
curl -G https://listingsapi.com/api/v4/locations-by-ids \--data-urlencode 'ids=["TG9jYXRpb246MTgwMDI4OQ==","TG9jYXRpb246MTgwMDI5MA=="]' \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationsByIds": [ { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "storeId": null, "name": "Jenny Home", "archived": false, "archivedAt": null, "city": "Austin", "stateIso": "TX", "countryIso": "US", "subCategoryId": 1432, "businessHours": [], "locationPhotos": [], "placeActionLinks": [], "discoveredPlaceActionLinks": [] } ] }}계정에 존재하지 않는 ID는 오류 없이 결과에서 제외됩니다. 반환된 개수와 전송한 개수를 비교하여 더 이상 유효하지 않은 ID를 찾습니다.
매장 코드로 사업장 조회
GET /locations-by-store-codes는 자체 매장 코드로 전체 레코드를 찾는 역방향 조회입니다. 일반 배열을 반환합니다. POS나 가맹점 명부에서 매장 코드를 사업장 식별 키로 사용하고 listingsAPI의 ID를 저장한 적이 없을 때 사용합니다.
매개변수
curl -G https://listingsapi.com/api/v4/locations-by-store-codes \--data-urlencode 'storeCodes=["ACME01","ACME02"]' \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationsByStoreCodes": [ { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "storeId": "ACME01", "name": "Jenny Home", "archived": false, "city": "Austin", "countryIso": "US", "planName": "Launch" } ] }}각 객체의 storeId를 명부의 해당 항목과 연결합니다. 일치하는 항목이 없는 코드는 포함되지 않습니다. 사업장을 생성하거나 업데이트할 때 storeId를 설정한 경우에만 해당 사업장에 매장 코드가 있습니다.
사업장 생성
POST /locations는 사업장을 생성하고 요금제에 포함된 모든 게시 플랫폼에 제출하도록 대기열에 추가하며, enabledSiteIds 필드를 전달하면 해당 필드에 지정한 게시 플랫폼에만 제출하도록 대기열에 추가합니다. 모든 필드는 input 아래에 넣습니다. 10개 필드는 필수이고 나머지는 선택 사항이며, 생략한 필드는 게시 플랫폼에 빈 값으로 표시됩니다.
매개변수
이 외에도 참조 페이지에 나열된 Google GCID 카테고리, 소셜 URL, 좌표, 소유자 연락처, 결제 수단, 서비스 지역 등의 선택 필드가 있습니다.
curl -X POST https://listingsapi.com/api/v4/locations \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "name": "Acme Kitchen", "description": "Acme Kitchen is a family-owned neighborhood restaurant in downtown New York serving wood-fired pizza, hand-rolled pasta, and a short seasonal menu built around produce from nearby growers. Our dining room seats forty, takeout and delivery run until close, and the bar pours regional wine and beer on tap.", "storeId": "ACME01", "street": "123 Jump Street", "city": "New York", "stateIso": "NY", "postalCode": "10013", "countryIso": "US", "phone": "6443859313", "bizUrl": "https://acmekitchen.example.com", "subCategoryId": 1432, "businessHours": [ { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] }, { "day": "SUNDAY", "type": "CLOSED", "slots": [] } ], "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true } ] }}'{ "data": { "createLocation": { "clientMutationId": null, "success": true, "errors": null, "location": { "id": "TG9jYXRpb246MTgwMDI5MA==", "databaseId": 1800290, "storeId": "ACME01", "name": "Acme Kitchen", "city": "New York", "countryIso": "US", "status": "PENDING", "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true, "name": null, "submissionStatus": null } ], "discoveredPlaceActionLinks": [] } } }}두 ID를 모두 보관합니다. base64 id는 이후의 모든 쓰기 작업에 필요하며, databaseId는 대시보드와 웹훅에 표시되는 번호입니다. status는 PENDING으로 시작하며 추가 API 호출 없이 다음 상태로 진행됩니다. 장소 작업 링크의 name과 submissionStatus는 해당 링크가 Google에 동기화될 때까지 null로 유지됩니다. Python의 locations.add()는 로컬에서 필수 필드의 유효성을 검사하고 네트워크 호출 전에 ValidationError를 발생시키며, locations.create(input)는 대신 원시 camelCase dict를 받습니다.
사업장 업데이트
POST /locations/update는 부분 업데이트입니다. input.id만 필수 필드이며, 전송한 필드만 변경됩니다. 같은 호출로 전화번호를 수정하거나 주간 일정을 다시 작성할 수 있습니다. Python의 locations.update()와 Node의 updateLocation()은 모두 camelCase 객체 1개를 받습니다.
매개변수
이 외에도 참조 페이지에 나열된 선택 필드를 사용할 수 있습니다.
curl -X POST https://listingsapi.com/api/v4/locations/update \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "phone": "5125550199", "bizUrl": "https://acmekitchen.example.com", "primaryGbpSiteCategoryId": "gcid:restaurant", "businessHours": [ { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] }, { "day": "SUNDAY", "type": "CLOSED", "slots": [] } ] }}'{ "data": { "updateLocation": { "clientMutationId": null, "success": true, "errors": null, "location": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "phone": "5125550199", "status": "APPROVED", "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.example.com", "isPreferred": true, "name": null, "submissionStatus": null } ], "discoveredPlaceActionLinks": [] } } }}응답은 업데이트된 사업장을 반환하므로 새 값이 적용되었는지 확인할 수 있습니다. 모든 업데이트는 사업장의 현재 placeActionLinks 전체 집합을 반환합니다. 링크의 URL을 변경하려면 동일한 요청에서 기존 링크에 대한 delete와 새 링크에 대한 add를 전송해야 합니다.
사업장 보관 처리
POST /locations/archive는 1개 이상의 사업장에 대한 보관 처리를 예약합니다. 보관 처리는 즉시 이루어지지 않습니다. 각 사업장은 ARCHIVE_SCHEDULED 상태로 전환되고, 현재 청구 주기가 끝날 때까지 활성 상태와 과금이 유지되며, 다음 청구서가 생성될 때 보관 처리됩니다. 폐점한 지점들을 일괄적으로 서비스에서 제외할 때 사용합니다.
매개변수
curl -X POST https://listingsapi.com/api/v4/locations/archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="] }}'{ "data": { "archiveLocations": { "clientMutationId": null, "bulkEditTaskId": null, "errors": null, "success": null, "result": [ { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null }, { "locationId": "TG9jYXRpb246MTgwMDI5MA==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null } ] } }}최상위 success는 null이므로 각 result[] 항목의 개별 success를 확인해야 하며, 잘못된 id 1개가 나머지 일괄 처리를 차단하지 않습니다.
보관 처리가 예약된 동안에는 목록 및 검색 결과에서 사업장의 archivalScheduledAt이 설정됩니다. 보관 처리된 사업장은 다시 활성화할 때까지 업데이트할 수 없습니다.
예약된 보관 처리 취소
POST /locations/cancel_archive는 ARCHIVE_SCHEDULED 상태의 사업장을 ACTIVE 상태로 되돌리며, 과금은 계속됩니다. 청구 주기가 아직 종료되지 않은 경우에만 작동하며, 사업장이 완전히 보관 처리된 후에는 이 호출이 해당 사업장에 대해 404를 반환합니다.
매개변수
curl -X POST https://listingsapi.com/api/v4/locations/cancel_archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="] } }'{ "data": { "cancelLocationsArchive": { "clientMutationId": null, "bulkEditTaskId": null, "errors": null, "success": null, "result": [ { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ACTIVE", "success": true, "errors": null } ] } }}각 result[] 항목은 사업장이 ACTIVE 상태로 돌아왔음을 확인해 줍니다. 두 SDK 모두 선택 유형과 changedBy 식별자라는 2개의 추가 위치 인수를 요구하고 이를 locationIds와 함께 전송하며, REST 본문에는 id만 필요합니다.
사업장에 사진 업로드
POST /locations/photos는 공개 URL을 통해 이미지를 첨부합니다. 이미지를 호스팅하면 API가 해당 이미지를 가져와 저장합니다. 사업장은 LOGO 1개, COVER 1개, 개수 제한 없는 ADDITIONAL 사진을 보관합니다. 로고나 커버를 다시 업로드하면 기존 이미지가 교체됩니다.
매개변수
curl -X POST https://listingsapi.com/api/v4/locations/photos \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photos": [ { "photo": "https://cdn.example.com/jenny-home/logo.png", "type": "LOGO" }, { "photo": "https://cdn.example.com/jenny-home/storefront.jpg", "type": "ADDITIONAL" } ] }}'{ "data": { "addLocationPhotos": { "clientMutationId": null, "success": true, "request": {}, "photos": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "databaseId": 5878931, "type": "LOGO", "starred": false, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.png" }, { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": false, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg" } ], "errors": null } }}별표 표시 및 삭제 엔드포인트가 각 사진의 id를 받으므로 이 값을 보관해야 합니다. 이는 소규모 일괄 처리에 사용되는 인라인 응답 형식입니다. 대규모 일괄 처리는 대신 대기열에 추가됩니다. photos는 비어 있는 상태로 반환되고 request.requestId가 설정되며, 해당 id로 다음 엔드포인트를 폴링합니다. 임계값은 서버 측 설정이므로 request.requestId의 존재 여부에 따라 분기해야 합니다.
사진 일괄 업로드 상태 확인
GET /locations/photos/requests/{requestId}는 대기열에 있는 업로드가 완료되거나 실패할 때까지 폴링한 다음 이미지별 결과를 보고합니다. 진행률 표시줄을 구현하고 어떤 파일이 어떤 이유로 거부되었는지 정확히 표시하는 데 사용합니다.
매개변수
curl https://listingsapi.com/api/v4/locations/photos/requests/e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationPhotosUploadStatus": { "requestId": "e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a", "status": "SUCCESS", "photos": [ { "mediaId": "TWVkaWFGaWxlOjkxODI3", "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "SUCCESS", "errorMessage": null }, { "mediaId": null, "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ERROR", "errorMessage": "Image dimensions below the minimum 250x250." } ] } }}작업 상태는 처리가 완료되었다는 의미일 뿐, 모든 이미지가 등록되었다는 의미는 아닙니다. 폴링 사이에 몇 초의 간격을 둡니다. 간격 없이 반복하면 금방 429가 반환됩니다.
사업장의 사진 조회
GET /locations/{locationId}/photos는 사업장의 모든 미디어 파일을 단순 배열로 반환합니다. 여기에는 로고, 커버 및 추가 이미지가 포함되며, 각 항목에는 공개 url, thumbnailUrl, 카테고리, starred 플래그 및 전체 기간의 viewsCount가 포함됩니다. 업로드 전에 호출하여 어떤 슬롯이 비어 있는지 확인하고, 별표 지정 및 삭제 호출에 사용할 id를 수집합니다.
매개변수
curl https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/photos \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "mediaFilesOfLocation": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "categoryName": "Logo", "categoryId": "4", "fileType": "image", "starred": false, "viewsCount": 0, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.jpg", "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_f47d62a6.jpg" }, { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "categoryName": "Additional", "categoryId": "12", "fileType": "image", "starred": true, "viewsCount": 143, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg", "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_ef4ffb12.jpg" } ] }}categoryName은 Logo, Cover 또는 Additional입니다. 그리드에는 thumbnailUrl을 사용하고 원본 크기 보기에는 url을 사용합니다. 미디어가 없는 사업장은 오류가 아닌 빈 배열을 반환합니다.
사업장 사진에 별표 표시
POST /locations/photos/star는 대표 이미지를 지원하는 게시 플랫폼에서 추가 사진이 우선 배치되도록 표시합니다. ADDITIONAL 사진에만 별표를 지정할 수 있으며, 사업장당 최대 4개까지 가능합니다. 별표 해제도 같은 방식으로 작동하며, 본문의 starred 플래그가 동작을 결정합니다.
매개변수
curl -X POST https://listingsapi.com/api/v4/locations/photos/star \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="], "starred": true }}'{ "data": { "starUnstarLocationPhotos": { "success": true, "photos": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": true } ], "error": null } }}두 경로 모두 응답 키는 starUnstarLocationPhotos이며, errors 배열이 아닌 단일 error를 포함합니다. 별표가 지정된 사진이 5개가 되면 전체 호출이 SY15012로 실패하며, 이 검사에서는 이미 별표가 지정된 사진과 이번 요청에 포함된 사진을 합산합니다.
사업장에서 사진 제거
POST /locations/photos/remove는 미디어 id를 사용하여 추가 사진의 연결을 해제합니다. 오래된 매장 외관 이미지를 제거하거나 잘못된 일괄 가져오기를 되돌리는 데 사용합니다. ADDITIONAL 사진만 삭제할 수 있으며, 로고나 커버를 교체하려면 해당 type으로 새 이미지를 업로드합니다.
매개변수
curl -X POST https://listingsapi.com/api/v4/locations/photos/remove \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="] }}'{ "data": { "removeLocationPhotos": { "clientMutationId": null, "removedCount": 1, "errors": null } }}removedCount는 실제로 연결이 해제된 파일 수를 보고하므로, errors: null과 함께 반환된 0은 해당 id 중 이 사업장에 연결된 것이 하나도 없었다는 의미입니다. 삭제는 파일의 연결을 해제하는 것이며, 미디어 라이브러리에서도 파일이 완전히 삭제되는 것은 아닙니다.
발생할 수 있는 오류
쓰기 작업에서도 success: false와 data.<operation>.errors의 상세 정보가 포함된 HTTP 200을 반환할 수 있습니다. 전체 목록은 오류 코드에 있습니다.
다음 단계
- 사업장 추가, 사업장 업데이트, 사업장 사진 업로드, 사업장 검색 및 보관 처리: 이 엔드포인트를 기반으로 하는 작업 가이드입니다.
- 국가 및 카테고리 조회: 생성에 필요한
countryIso,stateIso,subCategoryId값입니다. - 장소 작업 링크: 생성 및 업데이트 시 사용하는 Google 작업 버튼의 전체 모델입니다.
- 사업장: 리소스의 시작 페이지와 Python 및 Node SDK 참조 문서입니다.
- 사진 메서드에는 Python 사진과 Node 사진이라는 별도의 SDK 페이지가 있습니다.