Use Naver Map Business Listings with Menu & Hours Data with Python
Search Naver Map by keyword or place URL and get address, phone, weekly hours, menu with prices, amenities, transit access, booking links, and review scores for each business.
1. Choose the data you need
Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.
2. Prepare a source-based input
This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.
{
"emitUnchanged": false,
"hasBooking": false,
"hasCoupon": false,
"hasNpay": false,
"includeDetails": true,
"includeReviews": false,
"maxItems": 100,
"maxResultsPerKeyword": 20,
"maxReviewsPerPlace": 10,
"mode": "search",
"monitoringMode": false,
"newlyOpened": false,
"openNow": false,
"placeUrls": [],
"searchKeywords": [
"강남 맛집"
],
"sortBy": "relevance",
"takeout": false,
"wheelchairAccessible": false
}
Download input.example.json and save your reviewed copy as input.json.
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | required | What to fetch. `search` discovers places by keyword; `byUrls` fetches places you already know (URLs or place IDs). |
searchKeywords | array | optional | Keywords to search on Naver Map, e.g. `강남 맛집` (Gangnam restaurants) or `이태원 카페` (Itaewon cafes). Include a region name for local results. Naver returns up to 100 places per keyword from the first page and up to ~300 via deeper pages; add a map area (Search coordinates + Radius) or narrower keywords for more. |
placeUrls | array | optional | Naver Map places to fetch directly. Accepts full links (pcmap.place.naver.com, m.place.naver.com, map.naver.com/p/entry/place/...), `naver.me` short links, and bare numeric place IDs. |
sortBy | string | optional | How Naver orders search results. `distance` sorting needs Search coordinates. |
searchCoordinates | string | optional | Map center as `latitude,longitude`, e.g. `37.4979,127.0276` for Gangnam Station. Used to rank results around a point, for distance sorting, and as the center of an area sweep. |
searchRadiusKm | integer | optional | When set together with Search coordinates, the actor sweeps the whole circle (grid of map centers) instead of one center — this is how to collect more than Naver's ~300-per-keyword limit. Needs a broad keyword (e.g. `맛집`). |
category | string | optional | Only keep places whose category contains this text (e.g. `한식`, `카페`, `Cafe`). Leave blank for all categories. |
menuKeyword | string | optional | Only keep places whose menu names or descriptions contain this text (e.g. `피자`). Requires Include place details to be on. Leave blank for all menus. |
maxResultsPerKeyword | integer | optional | Cap on places emitted per keyword (before filters). First page gives up to 100 places; deeper Naver pages add up to ~300 total per map center, and an area sweep multiplies that per center. |
includeDetails | boolean | optional | Fetch each place's detail page for full weekly hours (breaks, last order, upcoming closures), full menus with prices, facilities, parking, photos, review breakdown and more. Slower but much richer data. |
includeReviews | boolean | optional | Emit real visitor reviews per place as separate `recordType: "review"` records (rating, text, dates, keywords, photos, owner replies). Uses Naver's cursor-paginated review API (50 per request) up to Max reviews per place; falls back to the ~20 reviews embedded on the public review page if that API is unavailable. Works in both modes. |
maxReviewsPerPlace | integer | optional | Cap on review records emitted per place when Include visitor reviews is on. Naver serves 50 reviews per request and does not expose a hard cap, so this can go high (e.g. 500); each 50 reviews costs one extra request. |
minVisitorReviewScore | number | optional | Only keep places with a visitor review score at or above this value (0-5). Leave blank for no filter. |
minVisitorReviewCount | integer | optional | Only keep places with at least this many visitor reviews. Leave blank for no filter. |
openNow | boolean | optional | Only keep places currently marked as open by Naver Map. |
hasBooking | boolean | optional | Only keep places that support online booking/reservations through Naver. |
hasNpay | boolean | optional | Only keep places that accept Naver Pay. |
hasCoupon | boolean | optional | Only keep places with an active coupon or promotion. |
takeout | boolean | optional | Only keep places marked as offering takeout (포장). |
newlyOpened | boolean | optional | Only keep places Naver marks as newly opened. |
wheelchairAccessible | boolean | optional | Only keep places with a wheelchair-accessible entrance. |
maxItems | integer | optional | Hard cap on total emitted place records across all keywords/URLs (review records are extra). |
monitoringMode | boolean | optional | Recurring-job mode: remembers each place between runs (in the Actor's key-value store) and emits only places that are new or whose name, category, address, phone, homepage, weekly hours, menus, or amenities changed. Adds `changeType` (NEW/UPDATED/UNCHANGED) and `changedFields` to each emitted place. Review counts and current open/closed status are ignored so runs are not billed for noise. |
monitoringStateKey | string | optional | Optional name for the monitoring state (mode=monitoringMode). Leave blank to derive the key from the run inputs, so the same keywords/URLs always compare against the same memory. |
emitUnchanged | boolean | optional | In Monitoring mode, also emit places with `changeType: "UNCHANGED"` instead of skipping them. |
3. Start an Actor run
Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.
Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.
import json
import os
import urllib.request
# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
actor_input = json.load(stream)
token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
"https://api.apify.com/v2/acts/l8lB2zLDoXJ2xjpAe/runs?timeout=120&maxTotalChargeUsd=0.50",
data=json.dumps(actor_input).encode("utf-8"),
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.
4. Inspect the actual results
Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.
Naver Map business records with full menus and prices, weekly hours with breaks and closures, address, phone, amenities, transit, booking, Michelin/TV mentions, and review analysis (plus optional visitor-review records).
Questions before you run
Which inputs are required?
The published required fields are mode
What does this guide verify?
The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.
What if the run request times out?
Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.
Generated from source build C7P2a9cthLY2sPU9H and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.