# Naver Map Reviews Scraper - Korean Ratings & Blog Posts (`s-r/naver-map-reviews`) Actor

Scrape reviews from any Naver Map place in South Korea. Returns visitor reviews with star rating, author, visit date, repeat-visit count, photos, menu ordered, voted keywords and the owner's reply, plus the Naver blog and cafe posts linked to the place. Give a place ID or a place URL.

- **URL**: https://apify.com/s-r/naver-map-reviews.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Travel, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Map Reviews Scraper

Pull the reviews for any place on Naver Map, South Korea's dominant local search
and mapping service. Give the actor a place ID or a place URL and it returns
clean, structured rows: who wrote the review, what they rated it, when they
visited, how many times they have been back, what they ordered, which photos
they posted, and what the owner wrote back.

It covers both kinds of review Naver publishes for a place, and it tells you
which kind each row is.

### Why Naver and not Google

For Korean local business data, Google Maps is the wrong map. Naver is where
Korean customers actually look up a restaurant, a clinic, a salon or a cafe,
and it is where they leave the review afterwards. A well-known Seoul cafe can
carry three thousand Naver visitor reviews and two thousand blog write-ups while
its Google listing shows a couple of hundred. If you are researching the Korean
market, doing competitor analysis on a Korean chain, or monitoring your own
branches in Korea, the Naver reviews are the dataset and the Google ones are the
footnote.

### The two kinds of review

Naver shows two separate review streams on every place page, with separate
counts, and they are genuinely different things.

**Visitor reviews** are the first-party reviews written on Naver itself. They
carry a star rating out of five, the reviewer's Naver nickname and profile, the
date of the visit, how many times that person has visited the place, the menu
item or service they paid for, the photos and videos they attached, the
sentiment keywords they voted for, and the owner's reply when there is one.
These are the rows you want for rating analysis, sentiment work or review
monitoring.

**Blog and cafe posts** are the long-form Naver blog and Naver cafe write-ups
that Naver has linked to the place. They have no star rating. What they have
instead is a headline, a link, the blogger's name and profile, a publication
date, a photo gallery and a body of text that often runs to several hundred
words about one visit. These are the rows you want for content research,
influencer discovery or understanding how a place is being talked about.

Both land in the same dataset, tagged by a `review_type` column, so you can take
one kind or both in a single run. The dataset ships with two saved views,
**Visitor reviews** and **Blog and cafe posts**, so each kind reads as a clean
table without the other one's columns getting in the way.

### Input

| field | what it does |
|---|---|
| **Place IDs or place URLs** | The places to read. A bare numeric ID or a full Naver place link both work, and you can mix them in one list. |
| **Which reviews** | Visitor reviews, blog and cafe posts, or both. |
| **Max reviews per place** | How many to return per place, per review type. |
| **Sort order** | Naver's recommended order, newest first, or by rating. Applies to visitor reviews. |

Finding a place ID takes one look at the address bar. Open the place on Naver
Map and the URL reads something like
`https://map.naver.com/p/entry/place/1668757640` — that number is the ID. You do
not have to extract it yourself; paste the whole URL and the actor will.

### Output

Every row carries the place it belongs to, so a run across many places stays
sortable.

#### Visitor review columns

`place_id`, `place_name`, `review_type`, `review_id`, `rating`, `body`,
`author_nickname`, `author_id`, `author_url`, `author_review_count`,
`author_photo_count`, `author_avg_rating`, `author_follower_count`,
`visited_at`, `visited_label`, `created_label`, `visit_count`, `view_count`,
`photo_count`, `video_count`, `media_urls`, `menu_item`, `origin_type`,
`voted_keywords`, `visit_context`, `owner_reply`, `language`, `status`,
`business_name_on_review`, `url`.

A few of those are worth a sentence. `visit_count` is how many times that
reviewer has been to this place, which is the single best loyalty signal in the
dataset. `menu_item` is what they actually ordered, so you can rate a dish
rather than a restaurant. `voted_keywords` are Naver's fixed sentiment tags in
the reviewer's own words. `author_review_count` and `author_avg_rating` let you
weight a review by how prolific and how generous its author is, which is how you
find the reviewers whose opinion moves a rating. `origin_type` says how the
review was verified, for example against a receipt.

#### Blog and cafe post columns

`place_id`, `place_name`, `review_type`, `review_id`, `title`, `body`,
`blog_url`, `blog_name`, `blog_home_url`, `author_nickname`,
`author_image_url`, `source_type`, `source_type_label`, `posted_date`,
`posted_label`, `rank`, `thumbnail_url`, `thumbnail_urls`, `photo_count`,
`is_representative`, `url`.

Blog rows carry no `rating` column, because Naver does not publish one for them.
An always-empty rating column would read as "nobody rated these", which would be
a claim about the posts rather than about what Naver publishes, so the column is
simply absent.

#### Korean text stays Korean

Review bodies, business names, nicknames, menu items and sentiment keywords come
back exactly as Naver published them. Nothing is transliterated or machine
translated on the way out. If you want English, translate downstream, where you
can see what the original said.

### Three ways a result can be empty, and how to tell them apart

This is the part most review scrapers get wrong, and it is the reason this
actor writes a run summary.

Naver answers a request for a place ID that does not exist with the same thing
it answers for a real place that nobody has reviewed: a successful response,
zero reviews, no error. A scraper that takes that at face value will tell you a
restaurant has no reviews when what actually happened is that you had a typo in
the ID. That is a confident wrong answer under a green run, and it is worse than
a failure, because a failure you would have investigated.

So every place is resolved against its own record before its reviews are read,
and the run summary keeps the three cases apart:

- **placesNotFound** — Naver has no place with that ID. The row is marked
  `found: false` and is not billed.
- **placesWithNoReviews** — the place is real, and it genuinely has no reviews.
- **placesBlocked** — the site did not serve us. The row is marked `blocked:
  true` and is not billed, because that is our problem and not your data.

Alongside the summary the run writes a per-place record listing what Naver says
each place's review counts are, next to how many were actually served.

### What Naver will and will not serve

Naver publishes a review count on the place page and then serves you fewer than
that. This is not a limitation of the actor; it is where Naver stops, and
pretending otherwise would be the same kind of confident wrong answer.

On a Seoul cafe with 3,177 visitor reviews and 2,078 blog posts on its page,
measured on 2026-09-16:

- the visitor review list served **3,000** distinct reviews and then ended;
- the blog post list served **109** and then ended, and Naver publishes that
  ceiling itself in the response.

Both ceilings vary by place — a smaller place will serve everything it has. What
matters is that the run summary names them. When the list Naver served was
shorter than the count Naver advertises, the place is listed under
`placesVisitorListTruncatedByNaver` or `placesBlogListCappedByNaver`, with both
numbers, so a short result is never mistaken for a place having lost its
reviews. Set **Max reviews per place** to whatever you need; the actor stops at
Naver's ceiling or yours, whichever comes first, and tells you which.

### What you can build with it

- **Review monitoring for Korean branches.** Run newest-first on a schedule and
  diff on `review_id` to catch every new review and every new owner reply.
- **Competitor rating analysis.** Pull the same number of recent reviews across
  a set of rivals and compare rating distributions rather than the single
  headline score, which hides the shape.
- **Menu and service level insight.** Group by `menu_item` and `rating` to find
  the dish that is dragging a restaurant's average down.
- **Loyalty and churn signal.** `visit_count` above one marks a returning
  customer; the share of returning reviewers is a better health metric than the
  rating.
- **Influencer discovery.** Sort blog rows by author and cross-reference
  `author_review_count` on the visitor side to find the people whose Korean
  write-ups actually carry reach.
- **Owner responsiveness benchmarking.** The share of reviews with a non-empty
  `owner_reply` is a direct measure of how engaged a business is, and it is a
  strong sales signal if you sell to Korean SMBs.
- **Training data for Korean sentiment models.** Star-rated Korean prose with
  sentiment keywords attached, which is unusually clean labelled text.

### Related actors

Pair this with **Naver Map Scraper** to go from a search term to a list of
places with IDs, then feed those IDs straight in here for the reviews.

### Notes

- Reviews are public data as published on Naver Map. Handle personal data in
  reviews in line with the rules that apply to you, including Korea's PIPA if
  you operate there.
- Rows are only billed when a review is actually delivered to you. A dead place
  ID, a place with no reviews, and a refused read all cost nothing.
- Free-plan runs receive a limited number of rows per run; paid plans run
  uncapped.

# Actor input Schema

## `placeIds` (type: `array`):

Naver Map places to read reviews for. Paste either the numeric place ID or the full place URL, for example https://map.naver.com/p/entry/place/1668757640 or https://pcmap.place.naver.com/restaurant/1668757640/home. Both forms work and mixing them is fine.

## `reviewType` (type: `string`):

Visitor reviews are the star-rated reviews people write on Naver itself. Blog reviews are the Naver blog and cafe posts Naver has linked to the place; they carry a title, a link and the post body but no star rating. Pick 'Both' to get each kind in one run, told apart by the review\_type column.

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

How many reviews to return for each place, per review type. Naver caps what it will serve regardless of what you ask for: roughly 3,000 visitor reviews per place, and a few hundred blog posts at most. The run summary reports the cap it hit so a short result is never mistaken for a place losing its reviews.

## `sort` (type: `string`):

How Naver orders the visitor reviews before they are read. 'Recommended' is Naver's own default ordering, 'Newest first' walks back from today, and 'By rating' leads with the highest ratings. Blog posts always come back in Naver's own order and ignore this setting.

## Actor input object example

```json
{
  "placeIds": [
    "1668757640"
  ],
  "reviewType": "visitor",
  "maxReviewsPerPlace": 100,
  "sort": "recommend"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per review, visitor and blog together, told apart by review\_type.

## `output` (type: `string`):

Counts, and which of the three kinds of empty happened: the place ID was dead, the place genuinely has no reviews, or the site refused us.

## `places` (type: `string`):

For each place: whether it exists, the review counts Naver states, how many were actually servable, and whether Naver capped the list.

## `errors` (type: `string`):

Dead place IDs, refused reads and input problems.

# 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 = {
    "placeIds": [
        "1668757640"
    ],
    "reviewType": "visitor",
    "maxReviewsPerPlace": 100,
    "sort": "recommend"
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/naver-map-reviews").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 = {
    "placeIds": ["1668757640"],
    "reviewType": "visitor",
    "maxReviewsPerPlace": 100,
    "sort": "recommend",
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/naver-map-reviews").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 '{
  "placeIds": [
    "1668757640"
  ],
  "reviewType": "visitor",
  "maxReviewsPerPlace": 100,
  "sort": "recommend"
}' |
apify call s-r/naver-map-reviews --silent --output-dataset

```

## MCP server setup

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

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/M3JcTdCKwVTeh9Sd7/builds/FSw3VJWAHc5Df5viY/openapi.json
