# Naver Map Reviews Scraper (Naver Place Reviews) (`magenta_courser/naver-place-reviews-scraper`) Actor

Give Naver Map (Naver Place) place IDs or URLs and get visitor reviews of Korean restaurants, cafes, clinics and shops: rating, text, visit date, keywords, menu, photos, owner reply, plus a rating summary per place. Reviewer identity is never collected.

- **URL**: https://apify.com/magenta\_courser/naver-place-reviews-scraper.md
- **Developed by:** [SUNGHWAN CHO](https://apify.com/magenta_courser) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 reviews

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Naver Place Reviews Scraper

Enter **Naver Place IDs or Naver Map URLs** and get **visitor reviews** as flat JSON rows: rating, review text, visit date and written date, keyword tags (Korean + English codes), ordered menu item, photos and the owner's reply, plus a status and summary row per place that tells you what was collected and why it stopped. Naver Map (Naver Place) is where people in South Korea review restaurants, cafes, hair salons, clinics and shops, and most reviews are verified by receipt, payment or booking. **Reviewer identity is never collected.**

### What you get

One row per review, in Naver's "recent" order (newest visit first) (`"type": "review"`):

- Star rating and full review text
- Visit date as an ISO timestamp (`visitedAt`), the date the review was written (`createdAt`, see Notes), plus Naver's original date labels
- Keyword tags chosen by the reviewer, in Korean and as English codes (`coffee_good`, `kind`, `store_clean`, …)
- Visit context (reservation, waiting time, companions, …) and the menu item or service ordered
- Verification type: `receipt`, `payment` or `booking`
- Which visit this was for the customer (`visitCount`), view count and reaction count
- Review photo URLs
- Owner reply text and `hasOwnerReply`
- Place ID, place name, place average rating and review count on every row

One status and summary row per place (`"type": "placeSummary"`, free, optional, on by default):

- `status` (`completed`, `partial`, `budgetLimited`, `notStarted`, `failed`) and `stopReason` (`endOfList`, `maxReviewsReached`, `reachedVisitDateCutoff`, `reachedPreviouslySavedReviews`, `costLimitReached`, `placeNotFound`, `requestFailed`)
- `reviewsSaved` (rows actually stored), `newReviewCount` (with `onlyNewSinceLastRun`), `ownerRepliesInSavedReviews` (owner replies among the reviews saved in this run — not a reply rate for the whole place)
- `placeCheckCharged` (`true` when this place was charged one place check, incremental mode only)
- Average rating, total review count, star distribution
- Mentioned themes (taste, service, mood, price, …) with counts
- Most mentioned menu items and keyword vote counts

One `runSummary` row per run (free) with totals, and the same report with a per-place list in the run's key-value store under `RUN_SUMMARY`, so code can check the result without reading the log.

Every row has `source: "naver"` and `schemaVersion: 2`. Missing values are `null`; keys are never left out.

No login, no API key and no browser needed.

**Not collected:** reviewer nicknames, profile pictures, profile links, user IDs and receipt links are never requested from Naver.

### Use cases

- **Reputation monitoring** — schedule a weekly run with `onlyNewSinceLastRun` and get only reviews the Actor has not delivered to you before (see the limits below)
- **Sentiment and topic analysis** — feed review text, `keywordCodes` and place themes into your own model or an LLM
- **Market research** — learn what Korean customers praise or complain about in a category before entering the market
- **Franchise and agency reporting** — compare ratings, keyword votes and owner reply activity across many locations
- **AI agents** — one place ID in, flat review rows out

#### Search → reviews

Don't have place IDs yet? Run [Naver Place Search Scraper](https://apify.com/magenta_courser/naver-place-search-scraper) with a keyword such as `성수 카페` (Seongsu cafes), then pass its `id` field straight into `places` here.

Managing many stores? [Naver Place Multi-Location Review Monitor](https://apify.com/magenta_courser/multi-location-review-monitor) reports only what changed across your locations (new reviews, owner replies added or edited) plus a free per-location summary of unanswered reviews and reply rate. Use this Actor when you want the raw review rows; use the monitor for a change and backlog report.

### Input

| Field | Description |
|---|---|
| `places` | **Required.** Naver Place IDs or Naver Map place URLs, one per line. Accepts `1922651675`, `https://map.naver.com/p/entry/place/1922651675`, `https://pcmap.place.naver.com/restaurant/1922651675/home` or `https://m.place.naver.com/hairshop/2005275293/review`. |
| `maxReviewsPerPlace` | Maximum reviews saved per place, newest visit first. Default 100. |
| `reviewsNewerThan` | Optional date (`YYYY-MM-DD`). **Visit-date filter**, read as 00:00 Korea time (KST). Only reviews whose visit date is on or after it are returned. It does not mean "written after": a review written today about a visit last month is skipped. API alias: `visitedAfter`. |
| `onlyNewSinceLastRun` | Default `false`. Return only reviews this Actor has not saved for you before (details below). In this mode each place checked is also charged one place check (see the Pricing tab), even when it has no new reviews. |
| `newReviewLookbackDays` | With `onlyNewSinceLastRun`: how far back by visit date to re-check before the previous completed run. Default 30. |
| `historyStoreName` | With `onlyNewSinceLastRun`: name of the key-value store holding saved review IDs. Default `naver-place-reviews-history`. Use one name per independent schedule. |
| `includePlaceSummary` | Add the free `placeSummary` rows and the `runSummary` row. Default `true`. |
| `proxyConfiguration` | Proxy settings. Keep the default Apify Proxy. |

```json
{
  "places": [
    "https://pcmap.place.naver.com/restaurant/1922651675/home",
    "2057596866"
  ],
  "maxReviewsPerPlace": 300,
  "onlyNewSinceLastRun": true,
  "includePlaceSummary": true
}
```

#### Only new reviews since last run — how it works and its limits

Naver's "recent" list is ordered by **visit date**, not by the date a review was written, and people often write a review days after the visit. So a date cut-off alone can miss new reviews. With `onlyNewSinceLastRun`:

1. For each place, the Actor keeps the IDs of reviews it **actually saved** (in the key-value store `historyStoreName` in your Apify account). Reviews that were cut off by your max cost per run or by an error are not recorded, so they are offered again next run.
2. The first run for a place saves the newest reviews up to `maxReviewsPerPlace` and becomes the starting point (`newReviewCount` is `null` on that run).
3. Later runs read pages from the newest visit downward, skip IDs already saved, and stop only after 20 already-known reviews in a row **and** visit dates older than the previous completed run minus `newReviewLookbackDays`. Unknown reviews whose `createdAt` is before the previous completed run (they existed then but were outside its scope, for example below its `maxReviewsPerPlace`) are skipped, not reported as new; reviews without a verified `createdAt` are treated as new.
4. `newReviewCount` on the `placeSummary` row and `previousCompletedAt` tell you how many new reviews were found and since when.
5. **Pricing in this mode:** you pay per review row saved **plus one place check per place checked** (prices on the Pricing tab). A place counts as checked once Naver returns its first review page; places that are not found, fail on the first page or are not started because of your max cost per run are not charged. A weekly run over 10 places with no new reviews costs 10 place checks and no review charges.

Late reviews are common: in a live check on 2026-10-01, a hair salon had reviews written on 2026-09-25 and 2026-09-26 for visits on 2026-08-30 and 2026-09-04.

Limits: a review written after the last run about a visit **older than the lookback window** is not found (raise `newReviewLookbackDays` if this matters). Running two schedules on the same places with the same `historyStoreName` makes them share one history. Hidden or deleted reviews are not reported. In our tests (fixtures and live runs), late-written reviews within the window were found and already-delivered reviews were not repeated; this is not a guarantee against every change Naver might make to its list.

### Output

Every row has a `type` field. A review row from a real run:

```json
{
  "type": "review",
  "source": "naver",
  "schemaVersion": 2,
  "placeId": "1922651675",
  "placeName": "노틀던",
  "placeRating": 4.97,
  "placeReviewCount": 1157,
  "reviewId": "6aafbfe99d2a23a1d03dd6dd",
  "rating": 5,
  "text": "너므너므 와보고 싶었던 노틀던!!!! 어쩜 이렇게 정교할까요 너무너무 예쁘고 맛잇습니다 서울숲디저트맛집 최고!!",
  "visitedAt": "2026-09-20T11:00:53.000Z",
  "createdAt": "2026-09-20T11:13:45.000Z",
  "visitedLabel": "9.20.일",
  "createdLabel": "9.20.일",
  "visitCount": 1,
  "viewCount": 268,
  "keywords": ["커피가 맛있어요", "디저트가 맛있어요", "특별한 메뉴가 있어요", "친절해요", "매장이 청결해요"],
  "keywordCodes": ["coffee_good", "dessert_good", "special_menu", "kind", "store_clean"],
  "visitContext": ["예약 없이 이용", "바로 입장", "데이트", "친목", "나들이", "일상", "연인・배우자", "친구", "지인・동료"],
  "menuItem": "Ice 아메리카노",
  "verificationType": "receipt",
  "photoCount": 2,
  "photoUrls": [
    "https://pup-review-phinf.pstatic.net/MjAyNjA5MjBfMTc5/MDAxNzg5OTAyODI0NTUz.pTzlm4A5O90SmpuahTwdtOG0fQlUH2tkXa2pWdVyl9Ug.P6-ycfaUuMAHU3WaANPSWZTSqsg4XcFkEmRMxm8KD6cg.JPEG/5E4323EF-203E-441C-AEE3-65FB0398B78B.jpeg?type=w1500_60_sharpen",
    "https://pup-review-phinf.pstatic.net/MjAyNjA5MjBfNCAg/MDAxNzg5OTAyODIxNTcw.OJ1Q8Yw9Gf4agzu8rDrioLzDyR_4VH63A12hxJdERdMg.CdmNkVgFgvRNYdt2dE2PmSM0AvM0cFZ4xq_FZrNDkvcg.JPEG/D08AEC9A-DDAC-4FB7-88CA-40B6802D5913.jpeg?type=w1500_60_sharpen"
  ],
  "hasOwnerReply": false,
  "ownerReply": null,
  "ownerReplyDateLabel": null,
  "reactionCount": 0,
  "language": "ko",
  "url": "https://map.naver.com/p/entry/place/1922651675?placePath=/review",
  "scrapedAt": "2026-10-01T09:04:51.939Z"
}
```

A place summary row from a run with `"reviewsNewerThan": "2026-09-25"` (the `themes`, `menuMentions` and `keywordVotes` arrays are shortened here to the first 3 items):

```json
{
  "type": "placeSummary",
  "source": "naver",
  "schemaVersion": 2,
  "placeId": "1922651675",
  "placeName": "노틀던",
  "status": "completed",
  "stopReason": "reachedVisitDateCutoff",
  "errorMessage": null,
  "reviewsSaved": 14,
  "newReviewCount": null,
  "ownerRepliesInSavedReviews": 0,
  "pagesFetched": 1,
  "previousCompletedAt": null,
  "avgRating": 4.97,
  "totalReviewCount": 1157,
  "listedReviewCount": 1089,
  "imageReviewCount": 943,
  "ratedReviewCount": 150,
  "starDistribution": { "0.5": 0, "1.0": 0, "1.5": 0, "2.0": 0, "2.5": 0, "3.0": 0, "3.5": 0, "4.0": 4, "4.5": 1, "5.0": 145 },
  "themes": [
    { "code": "taste", "label": "맛", "count": 812 },
    { "code": "total", "label": "만족도", "count": 608 },
    { "code": "service", "label": "서비스", "count": 152 }
  ],
  "menuMentions": [
    { "menu": "커피", "count": 172 },
    { "menu": "케이크", "count": 170 },
    { "menu": "밀푀유", "count": 66 }
  ],
  "keywordVotes": [
    { "code": "dessert_good", "keyword": "디저트가 맛있어요", "count": 1079 },
    { "code": "coffee_good", "keyword": "커피가 맛있어요", "count": 598 },
    { "code": "special_menu", "keyword": "특별한 메뉴가 있어요", "count": 590 }
  ],
  "keywordVoteCount": 4449,
  "url": "https://map.naver.com/p/entry/place/1922651675?placePath=/review",
  "scrapedAt": "2026-10-01T09:05:03.631Z"
}
```

Export the dataset as JSON, CSV or Excel, or read it through the Apify API. To get reviews only, filter rows where `type` is `review` or set `includePlaceSummary` to `false` (the run report stays available in `RUN_SUMMARY`).

#### Status and cost limit

Charged: each `review` row, and — only with `onlyNewSinceLastRun: true` — one `place-check` event per place checked (see the Pricing tab for prices). `placeSummary` and `runSummary` rows are free, and the default mode has no place-check charge. If the run reaches your **max cost per run**, the Actor stops cleanly: the place being scraped gets `status: "budgetLimited"`, the remaining places get `"notStarted"` (they are not requested and not charged), and `reviewsSaved` counts exactly the review rows stored. `placeCheckCharged` on each `placeSummary` row and `placeChecksCharged` on the `runSummary` row show the place checks charged. The run's status message says the same.

### Tips

- **Weekly monitoring:** schedule the Actor with `onlyNewSinceLastRun: true`. Check `status` / `stopReason` on the `placeSummary` rows (or `RUN_SUMMARY`) to see whether each place was fully checked.
- **Large places:** in our tests the Actor paged through 3,100 reviews of a single place with no duplicates and no cap. Set `maxReviewsPerPlace` as high as you need.
- **Many businesses work:** tested on restaurants, cafes, hair salons, dermatology clinics and hospitals. For hair salons, `menuItem` usually holds the service or designer booked.
- Text values (review text, keywords, menu names) are in Korean, as shown on Naver. Use `keywordCodes` and theme `code` for English, language-independent analysis.
- Missing values are `null`, numbers are numbers, and `visitCount` is a number (not "2번째 방문").
- Invalid lines in `places` are skipped with a warning in the log; the other places are still scraped.

### Notes

- **Newest visit first only.** Naver's "recent" order is by visit date (with small out-of-order spots within a day); Naver's "recommended" order is not supported.
- **Time zones:** `visitedAt`, `createdAt` and `scrapedAt` are UTC ISO timestamps (`Z`). Naver's labels (`visitedLabel`, `createdLabel`, `ownerReplyDateLabel`) are Korea time (KST, UTC+9). Date inputs are read as 00:00 KST.
- **`createdAt`** is derived from the timestamp inside Naver's review ID and is returned only when its Korea-time date matches Naver's own "written" label (`createdLabel`); otherwise it is `null`. In our checks it matched on every sampled review. For booking reviews, `visitedAt` is the booked time slot, so `createdAt` can be a little earlier than `visitedAt`.
- **Hospitals and clinics:** Naver hides star ratings for medical businesses, so `rating`, `placeRating`, `avgRating` and `starDistribution` are `null` there. Review text and keywords are still returned.
- About 15% of restaurant and cafe reviews have no star rating (`rating: null`), and photo-only reviews have `text: null`.
- **Owner reply dates have no year** on Naver (e.g. `9.30.수`), so they are kept as the original label in `ownerReplyDateLabel`.
- `totalReviewCount` (Naver's headline count) can be higher than `listedReviewCount` (reviews Naver actually lists); the scraper can only return listed reviews.
- This Actor collects only publicly visible review content. It does not log in, and it never requests reviewer nicknames, profile images, profile links, user IDs or receipt links. Review text is written by users and may still contain personal details they chose to share; handle it accordingly.

# Actor input Schema

## `places` (type: `array`):

Naver Place IDs or Naver Map place URLs, one per line. Accepted formats: a numeric ID such as 1922651675; https://map.naver.com/p/entry/place/1922651675; https://pcmap.place.naver.com/restaurant/1922651675/home; https://m.place.naver.com/hairshop/2005275293/review. The "id" field from Naver Place Search Scraper works directly. Short naver.me links are not supported.

## `maxReviewsPerPlace` (type: `integer`):

Maximum number of visitor reviews to save for each place, in Naver's "recent" order (newest visit first). Integer from 1 to 100000. Default 100. Example: 1000. With "Only new reviews since last run" on, this limits the new reviews saved per place.

## `reviewsNewerThan` (type: `string`):

Optional date in YYYY-MM-DD format, e.g. 2026-09-01. Filters by the VISIT date (visitedAt), not by when the review was written, read as 00:00 Korea time (KST, UTC+9). Paging stops shortly after the list passes this date. A review written today about a visit before this date is NOT returned - to get reviews written since your last run, use "Only new reviews since last run" instead. Leave empty for no date filter. API alias: visitedAfter.

## `onlyNewSinceLastRun` (type: `boolean`):

true or false. Default false. When true, the Actor remembers, per place, the IDs of reviews it actually saved (in a named key-value store in your account) and returns only reviews it has not saved before. The first run for a place saves the newest reviews up to the limit and sets the starting point. Later runs re-check recent pages with an overlap, so reviews written late about an earlier visit are still found if the visit is within the lookback window. Reviews cut off by your max cost per run are not remembered and come back next run. Pricing in this mode: per review saved plus one place check per place checked, even with no new reviews (see the Pricing tab). Places not found, failed on the first page, or not started are not charged.

## `newReviewLookbackDays` (type: `integer`):

Used only with "Only new reviews since last run". How many days before the previous completed run to keep re-checking by visit date. A review written after the last run about a visit older than this is not found. Default 30. Larger values make each run read more pages (no extra review charges).

## `historyStoreName` (type: `string`):

Used only with "Only new reviews since last run". Name of the key-value store in your Apify account that keeps the saved review IDs. Default naver-place-reviews-history. Use a different name for each independent schedule that monitors the same places, otherwise one schedule's run marks reviews as already seen for the other. Letters, digits and "-" only.

## `includePlaceSummary` (type: `boolean`):

true or false. Default true. Adds free (not charged) rows: one "placeSummary" row per place (status, stopReason, reviewsSaved, newReviewCount, average rating, review counts, star distribution, themes, menu mentions, keyword votes) and one "runSummary" row per run. Filter rows by the "type" field. The same status is always saved in the run's key-value store as RUN\_SUMMARY.

## `proxyConfiguration` (type: `object`):

Proxy settings. Keep the default Apify Proxy (datacenter); Naver rate-limits repeated requests from one IP.

## Actor input object example

```json
{
  "places": [
    "1922651675"
  ],
  "maxReviewsPerPlace": 100,
  "onlyNewSinceLastRun": false,
  "newReviewLookbackDays": 30,
  "includePlaceSummary": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `reviews` (type: `string`):

All scraped reviews and place summary rows as dataset items.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "places": [
        "1922651675"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("magenta_courser/naver-place-reviews-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "places": ["1922651675"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("magenta_courser/naver-place-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "places": [
    "1922651675"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call magenta_courser/naver-place-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,magenta_courser/naver-place-reviews-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XbRNrZyRkJzyZn0AQ/builds/ZgoPQpbe1KThj7zZm/openapi.json
