listings scan으로 업체 등록 정보 점검
어떤 업체든 지도, 검색 엔진, 디렉터리, AI 어시스턴트에 어떻게 표시되는지 확인하고, 디렉터리별 이름, 주소, 전화번호의 정확성을 확인하며, 웹훅 또는 폴링으로 결과를 받으세요.
listings scan은 한 업체가 listingsAPI 디렉터리 네트워크에 어떻게 등록되어 있는지 확인하고, 디렉터리별로 해당 업체가 검색되는지, 업체명, 주소, 전화번호가 제공한 정보와 일치하는지 알려줍니다. 해당 업체가 계정에 사업장으로 등록되어 있지 않아도 됩니다. 이 가이드에서는 스캔을 생성하고, 완료될 때까지 기다린 다음 결과를 확인합니다.
다음 용도로 사용하세요:
- 잠재 고객을 온보딩하기 전에 점검하여, 누락되거나 잘못된 등록 정보를 바탕으로 첫 상담을 시작합니다;
- 고객의 등록 정보를 수정하기 전과 후에 등록 현황과 NAP(업체명, 주소, 전화번호) 정보의 일관성을 확인합니다;
- 업체를 계정에 추가하지 않고도 AI 에이전트가 "이 업체는 어떻게 등록되어 있나요?"라는 질문에 답할 수 있게 합니다.
1. 스캔 생성
POST /listings/scans은 업체 정보를 단일 input 객체로 받아, QUEUED 상태의 스캔을 즉시 반환합니다. 디렉터리는 백그라운드에서 확인합니다.
curl -X POST https://listingsapi.com/api/v4/listings/scans \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d @- <<'JSON'{"input": { "name": "Joe's Pizza", "street": "7 Carmine Street", "city": "New York", "stateIso": "NY", "postalCode": "10014", "countryIso": "US", "phone": "2123661182"}}JSON응답에는 phone이 국제 형식으로 정규화된 스캔이 반환됩니다. 디렉터리를 확인하기 전까지 results은 비어 있으므로 id을 보관하세요.
{ "data": { "createListingScan": { "reused": false, "scan": { "id": "TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz", "status": "QUEUED", "source": "DIRECTORY_SEARCH", "locationId": null, "resultsAsOf": null, "createdAt": "2026-11-05T07:01:08Z", "startedAt": null, "completedAt": null, "business": { "name": "Joe's Pizza", "street": "7 Carmine Street", "city": "New York", "stateIso": "NY", "postalCode": "10014", "countryIso": "US", "phone": "+12123661182" }, "summary": { "total": 0, "found": 0, "accurate": 0, "inaccurate": 0, "notFound": 0, "pending": 0, "unavailable": 0 }, "failure": null, "results": [] }, "errors": [] } }}응답에는 비용, 제한 및 오류에서 설명하는 quota 객체도 포함됩니다.
다음 단계로 넘어가기 전에 2가지를 확인하세요:
reused. 최근 7일 이내에 스캔한 업체에 대해 요청하면, listingsAPI은 새 스캔을 시작하는 대신reused: true및 현재 상태와 함께 기존 스캔을 반환하며, 현재 상태는 대개COMPLETED입니다. 할당량은 차감되지 않으며, 해당 스캔에 대한 새 웹훅도 전송되지 않습니다.errors. 요청이 거부되어도 HTTP200을 반환합니다. 거부 내용은data.createListingScan.errors에 있으며,scan은null입니다. 전화번호가 너무 짧으면 다음과 같이 표시됩니다:
{ "data": { "createListingScan": { "reused": false, "scan": null, "errors": [ { "code": "SY83003", "message": "SY83003: Invalid value for phone", "contextInfo": [ { "key": "field", "value": "phone" } ] } ] } }}contextInfo은 검증에 실패한 필드의 이름을 나타냅니다. 반환될 수 있는 다른 코드는 오류 표에 나와 있습니다.
2. 결과 대기
스캔은 QUEUED에서 IN_PROGRESS을 거쳐 COMPLETED 또는 FAILED 상태로 전환됩니다. 대부분 1~7분 내에 완료됩니다. 약 15분이 지나도 디렉터리에서 응답하지 않으면 해당 디렉터리의 결과는 UNAVAILABLE으로 설정되고, 나머지 결과와 함께 스캔이 완료됩니다. 가능하면 웹훅을 사용하고, 대체 수단으로만 폴링을 사용하세요.
2a. 웹훅 수신(권장)
스캔이 최종 상태에 도달하면 listingsAPI은 웹훅 URL로 listing_scan.completed 또는 listing_scan.failed 이벤트를 1개 전송합니다. 이벤트의 data.scan은 결과를 포함하여 GET이 반환하는 객체와 동일하므로 이후에 추가로 조회할 필요가 없습니다.
{ "event": "listing_scan.completed", "timestamp": "2026-11-05T07:01:52Z", "account_id": 12345, "data": { "scan": { "id": "TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz", "status": "COMPLETED", "summary": { "total": 62, "found": 21, "accurate": 18, "inaccurate": 2, "notFound": 41, "pending": 0, "unavailable": 0 }, "results": [ "..." ] } }}여기서는 페이로드를 축약했으며, 전체 예제는 이벤트 참조 문서에 있습니다. 이벤트는 다른 모든 이벤트와 동일하게 서명되어 전달되며, 전달은 1회 시도하고 제한 시간은 5초이므로, 다른 처리를 시작하기 전에 서명을 검증하고 원본 본문을 저장한 다음 수신 확인 응답을 보내세요. 웹훅으로 이벤트에 대응하기의 수신 코드는 이미 이 작업을 수행하므로, event에 대한 분기만 1개 추가하면 됩니다.
# After the signature check and the endpoint.verification branch:if body.get("event") in ("listing_scan.completed", "listing_scan.failed"): inbox.put(raw) # commit before the 200; read data.scan from the queue return {"received": True}, 200이벤트는 웹훅이 활성화되어 있고 검증된 URL이 있는 계정에만 전송됩니다. 계정이 이 조건을 충족하지 않거나 이벤트가 도착하지 않으면 스캔을 폴링하세요. 어느 경우든 스캔은 90일 동안 보관됩니다. 서명에 관한 자세한 내용은 서명 및 검증에 있습니다.
2b. 대체 수단으로 폴링하기
GET /listings/scans/{scanId}은 현재까지 나온 결과와 함께 스캔을 반환합니다. 스캔이 실행되는 동안 각 디렉터리에서 응답이 오면 results이 채워지고, summary.pending는 아직 응답을 기다리는 디렉터리 수를 나타냅니다. 15초마다 폴링하고, 그보다 짧은 간격으로 폴링하지 마세요. 각 요청은 요청 빈도 제한에 포함됩니다.
SCAN_ID="TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz" while true; doscan=$(curl -s "https://listingsapi.com/api/v4/listings/scans/$SCAN_ID" \ -H "Authorization: API $LISTINGSAPI_KEY")status=$(echo "$scan" | jq -r '.data.listingScan.status')echo "status: $status"case "$status" in COMPLETED|FAILED) echo "$scan" | jq '.data.listingScan | {status, summary, failure}' break ;; null) echo "$scan" break ;;esacsleep 15done최근 스캔 결과나 본인 소유 사업장 중 하나의 정보로 결과가 제공되는 스캔은 생성 즉시 COMPLETED 상태가 되므로, GET을 1번만 호출하면 됩니다.
3. 결과 읽기
완료된 스캔에는 summary이 있으며, results에는 확인한 디렉터리마다 항목이 1개씩 포함되고 국가에 따라 최대 약 60개까지 포함됩니다. business는 전송한 입력을 반환하며, phone은 국제 형식으로 정규화됩니다.
{ "total": 62, "found": 21, "accurate": 18, "inaccurate": 2, "notFound": 41, "pending": 0, "unavailable": 0}total은 디렉터리 수입니다. found은 업체가 등록된 디렉터리 수이며, accurate, inaccurate, 그리고 아래에서 설명하는 존재 여부만 확인된 등록 정보로 나뉩니다. notFound는 업체가 등록되지 않은 디렉터리 수입니다. unavailable는 이번에 확인하지 못한 디렉터리 수이며, pending은 스캔이 실행되는 동안에만 0이 아닌 값입니다.
각 결과에는 해당 디렉터리의 status이 있습니다.
FOUND 결과에서 accurate은 등록 정보가 입력과 일치하는지를 나타냅니다.
true은 이름, 주소, 전화번호가 모두 일치한다는 의미입니다.false은 하나 이상이 일치하지 않는다는 의미입니다. 등록 정보에서 빠진 필드는 불일치로 처리되므로, 전화번호가 없는 등록 정보는false입니다.null은 업체를 찾았지만 필드별 확인은 수행하지 않았다는 의미입니다.
accuracy은 name, address, phone 각각에 대한 status과 message로 판정 내용을 설명합니다. status은 ACCURATE, INACCURATE 또는 MISSING이며, 필드가 정확하면 message은 null입니다. 다음은 이름과 주소가 일치하지만 전화번호가 없는 Facebook 페이지입니다.
{ "directory": { "id": 184, "name": "Facebook", "url": "facebook.com" }, "status": "FOUND", "accurate": false, "accuracySource": "DIRECTORY_MATCH", "accuracy": { "name": { "status": "ACCURATE", "message": null }, "address": { "status": "ACCURATE", "message": null }, "phone": { "status": "MISSING", "message": "No phone number on the listing" } }, "listing": { "name": "Joe's Pizza", "street": "7 Carmine St", "city": "New York", "state": "NY", "postalCode": "10014", "country": "US", "fullAddress": "7 Carmine St, New York, NY 10014", "phones": [], "url": "https://www.facebook.com/177353602413952" }, "syncStatus": null}일부 디렉터리는 업체의 존재 여부만 확인하며, 비교에 필요한 세부 정보를 충분히 표시하지 않습니다. 이러한 결과는 accurate: null이 포함된 FOUND으로 반환되며, found에만 집계되고 accurate이나 inaccurate에는 집계되지 않습니다. 이러한 결과는 존재가 확인된 것으로 처리하고, 정보가 검증된 것으로 처리하지 마세요.
{ "directory": { "id": 374, "name": "Map Quest", "url": "www.mapquest.com" }, "status": "FOUND", "accurate": null, "accuracySource": "DIRECTORY_MATCH", "accuracy": { "name": { "status": "ACCURATE", "message": null }, "address": { "status": "MISSING", "message": "Not shown" }, "phone": { "status": "MISSING", "message": "Not shown" } }, "listing": { "name": "Joe's Pizza", "street": "7 Carmine St", "city": "New York", "state": "NY", "postalCode": "10014", "country": null, "fullAddress": "7 Carmine St, New York, NY 10014, US", "phones": [], "url": null }, "syncStatus": null}NOT_FOUND 결과에는 listing과 accuracy가 없습니다.
{ "directory": { "id": 352, "name": "YellowPages", "url": "www.yellowpages.com" }, "status": "NOT_FOUND", "accurate": null, "accuracySource": "DIRECTORY_MATCH", "accuracy": null, "listing": null, "syncStatus": null}일부 디렉터리는 자체 데이터를 보유하지 않습니다. Uber, Waze, Lyft는 Google Maps 데이터를 표시하고, Copilot은 Bing 데이터를 표시합니다. 이러한 디렉터리의 결과는 원본 디렉터리의 결과를 반영하므로 대개 Google Maps나 Bing이 반환한 결과와 같으며, 등록 정보의 url이 없습니다. summary에서는 별도 항목으로 집계하되, 독립적으로 확인된 결과로 해석하지 마세요.
{ "directory": { "id": 350, "name": "Uber", "url": "uber.com" }, "status": "FOUND", "accurate": true, "accuracySource": "DIRECTORY_MATCH", "accuracy": { "name": { "status": "ACCURATE", "message": null }, "address": { "status": "ACCURATE", "message": null }, "phone": { "status": "ACCURATE", "message": null } }, "listing": { "name": "Joe's Pizza", "street": "7 Carmine Street", "city": "New York", "state": "NY", "postalCode": "10014", "country": "US", "fullAddress": "7 Carmine St, New York, NY 10014, USA", "phones": [ "+1 212-366-1182" ], "url": null }, "syncStatus": null}ChatGPT와 Gemini 같은 AI 어시스턴트도 포함됩니다. 이 경우 listing은 어시스턴트가 해당 업체에 대해 알고 있는 정보를 보여 주며, 이름, 전체 주소, 전화번호만 포함되는 경우가 많고 필드별 accuracy은 없습니다.
완료된 스캔으로 수정 목록을 만들려면 잘못되거나 누락된 필드가 있는 모든 디렉터리를 출력하세요.
curl -s https://listingsapi.com/api/v4/listings/scans/TGlzdGluZ1NjYW46Mzk0YTRhNWMtZjcwMy00NjZiLTg2NjAtMDU4MTlmNDhkZjMz \-H "Authorization: API $LISTINGSAPI_KEY" \| jq -r '.data.listingScan.results[] | select(.accurate == false) | .directory.name as $directory | .accuracy | to_entries[] | select(.value.status != "ACCURATE") | $directory + " " + .key + ": " + (.value.message // .value.status)'Joe's Pizza 스캔에서는 Facebook에 전화번호가 없고 Judys Book에 다른 전화번호가 표시된다는 내용이 2줄로 출력됩니다.
Facebook phone: No phone number on the listingJudys Book phone: Phone number does not match결과의 출처
source는 스캔 결과가 어떤 방식으로 제공되었는지를 나타내고, resultsAsOf은 데이터가 수집된 시점입니다.
비용, 제한 및 오류
새로 실행하는 DIRECTORY_SEARCH 스캔만 월간 허용량을 사용하며, 이 허용량은 요금제에 따라 달라지고 결제 주기에 맞춰 초기화됩니다. 다음은 무료입니다:
- 7일 이내에 동일한 비즈니스를 다시 스캔하는 경우입니다. 직접 실행한 이전 스캔이
reused: true와 함께 반환됩니다; - 어느 계정에서든 최근에 실행한 동일한 비즈니스의 스캔으로 결과를 제공할 수 있는 경우입니다. 이 결과는
RECENT_SCAN복사본입니다; - 계정 내 사업장 중 하나와 일치하는 스캔입니다(
LOCATION_LISTINGS); FAILED상태로 종료된 스캔이며, 사용량이 환급됩니다.
스캔은 90일 동안 보관됩니다. 이후에는 GET가 SY83005와 함께 404을 반환합니다.
생성 및 목록 조회 응답에는 quota 객체가 포함되므로 일괄 작업을 시작하기 전에 사용 현황을 확인할 수 있습니다. 목록 조회는 perPage을 포함하고 results는 제외하며, 페이지당 1~50개의 스캔을 최신순으로 반환합니다.
curl -s "https://listingsapi.com/api/v4/listings/scans?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY" \| jq '.data.listingScans.quota'가장 자주 발생하는 오류는 다음과 같습니다. 생성 작업은 HTTP 200과 함께 본문에 오류를 반환하고, 조회 및 목록 조회 작업은 일반적인 HTTP 상태를 반환합니다.
존재하지 않는 id에 대한 GET는 다음을 반환합니다:
{ "data": { "listingScan": null }, "errors": [ { "message": "SY83005: Listing scan not found" } ]}FAILED 상태로 종료된 스캔은 failure에 사유가 포함되며, results는 비어 있습니다:
{ "status": "FAILED", "failure": { "code": "SY83010", "message": "SY83010: Listing scans are temporarily unavailable, try again later", "field": null }}일반적인 오류 형식은 오류 코드에 설명되어 있습니다.
AI 에이전트용
호스팅된 MCP 서버는 이 기능을 listings_scans_create, listings_scans_get, listings_scans_list의 3가지 도구로 제공합니다. 어시스턴트는 사용자가 지정한 비즈니스의 스캔을 시작하고, listings_scans_get로 주기적으로 조회하며, 확인이 필요한 디렉터리를 요약할 수 있습니다. REST와 마찬가지로 스캔을 생성하려면 Write 키가 필요합니다. 설정 방법은 AI 에이전트를 listingsAPI MCP에 연결하기에 설명되어 있습니다.
다음 단계
- 웹훅으로 이벤트에 대응하기: 스캔 이벤트를 받는 수신기를 설명합니다.
- 등록 정보 상태 및 실제 링크 확인하기 및 등록 정보 오류 찾기 및 수정하기: 비즈니스가 계정에 사업장으로 등록되면 해당 등록 정보를 추적하고 수정하세요.
- 사업장 추가하기: 점검한 비즈니스를 등록하세요.
- 참조: listings scan 생성하기, listings scan 조회하기, listings scans 목록 조회하기, 이벤트 참조, 오류 코드.