# Naver Place Rank Tracker (네이버 플레이스 순위) (`magenta_courser/naver-place-rank-tracker`) Actor

Enter Korean keywords like 강남 맛집 and your Naver Place IDs or links; get your place's rank in Naver Map search (up to rank 300) with rank change since the last run, optionally the full ranked list.

- **URL**: https://apify.com/magenta_courser/naver-place-rank-tracker.md
- **Developed by:** [SUNGHWAN CHO](https://apify.com/magenta_courser) (community)
- **Security:** Limited permissions
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 50 ranks checkeds

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 Place Rank Tracker (네이버 플레이스 순위 · 네이버 지도 순위 조회)

Enter Korean search keywords (e.g. `강남 맛집`, `성수 카페`) and your Naver Place IDs or links, and get your place's rank in Naver Map / Naver Place search for each keyword, with the change since your last run. Optionally get the full ranked list (up to rank 300) with name, category, address, rating and review counts for every place.

Built for local SEO agencies (플레이스 상위노출 대행사), franchise HQs and shop owners who check "what rank is my shop for 강남 맛집?" every day or week.

### What you get

- **Your rank per keyword** — one row per keyword and place: `rank`, `found`, `previousRank`, `rankChange` (positive = moved up), `isNewEntry`
- **Rank history without spreadsheets** — the last ranks are kept in a named key-value store in your account, so a daily schedule lines up automatically
- **Full ranked list (optional)** — every place up to `maxRank` with rank change, category, address, rating, visitor and blog review counts
- **Search location (optional)** — ranks as seen from given coordinates, for keywords without an area name (`카페`, `미용실`)
- **Match by ID, link or name** — place ID, `map.naver.com` / `m.place.naver.com` link, `naver.me` short link, or the place name
- **Cheap, fast, no browser** — about 3 seconds per 50 ranks, 256 MB memory

### Input

| Field | Required | Description |
|---|---|---|
| `keywords` | yes | Naver Map search keywords, one per line, e.g. `강남 맛집`, `성수 카페`, `홍대 미용실` |
| `myPlaces` | no | Your place IDs or links, e.g. `1501340733` or `https://map.naver.com/p/entry/place/1501340733` |
| `myPlaceNames` | no | Match by name when you do not know the ID, e.g. `생활생돈` (also matches `생활생돈 선릉본점`) |
| `maxRank` | no | Scan depth, default 50, max 300 |
| `searchLocation` | no | `"latitude,longitude"`, e.g. `37.4979,127.0276` (Gangnam Station). Empty = Naver's default |
| `includeTopList` | no | Also output every ranked place. On automatically when no places are given |
| `compareWithLastRun` | no | Default `true`: add `previousRank`, `rankChange`, `isNewEntry` |
| `historyStoreName` | no | Named key-value store for the rank history, default `naver-place-rank-history`. Use one per client or project |

Example:

```json
{
  "keywords": ["강남 맛집", "선릉 맛집", "역삼 고기집"],
  "myPlaces": ["1501340733"],
  "maxRank": 100
}
```

**How to find your place ID:** open your place on [map.naver.com](https://map.naver.com) and copy the number after `/place/` in the address bar, or paste the whole link.

### Output

#### `type: myPlaceRank` — one row per keyword and tracked place

```json
{
  "schemaVersion": 1,
  "source": "naver-place",
  "type": "myPlaceRank",
  "keyword": "강남 맛집",
  "searchLocation": null,
  "placeId": "1501340733",
  "matchedBy": "id",
  "inputName": null,
  "nameMatches": null,
  "found": true,
  "rank": 1,
  "previousRank": 1,
  "rankChange": 0,
  "isNewEntry": false,
  "previousScrapedAt": "2026-10-29T13:24:20.202Z",
  "placeName": "생활생돈 선릉본점",
  "category": "돼지고기구이",
  "address": "서울특별시 강남구 역삼동 697-44 지상1층",
  "visitorRating": 4.94,
  "visitorReviewCount": 612,
  "blogReviewCount": 285,
  "ranksScanned": 50,
  "maxRank": 300,
  "totalResults": 34394,
  "searchStatus": "completed",
  "stopReason": "allMyPlacesFound",
  "url": "https://map.naver.com/p/entry/place/1501340733",
  "searchUrl": "https://map.naver.com/p/search/%EA%B0%95%EB%82%A8%20%EB%A7%9B%EC%A7%91",
  "scrapedAt": "2026-10-29T13:30:27.020Z"
}
```

- `found: true` — the place is within the scanned ranks; `rank` is its position.
- `found: false` — the scan finished and the place is not within `ranksScanned` ranks. Try a larger `maxRank`.
- `found: null` — the scan did not finish (see `searchStatus` and `stopReason`), so absence is unknown.
- `rankChange` = `previousRank − rank`: **positive means the place moved up**. `null` when there is no previous rank (first run, or the place was not in the previous scan).
- `isNewEntry: true` — the place is ranked now but was not within the depth the previous run scanned. `null` when that cannot be decided (first run, or ranked deeper than the previous scan).
- `placeName`, `category`, address and review fields are `null` when the place is not found.
- For name matches (`matchedBy: "name"`), the row shows the best-ranked match and `nameMatches` = how many listed places matched.

#### `type: rankedPlace` — only with `includeTopList`

`keyword`, `searchLocation`, `rank`, `previousRank`, `rankChange`, `isNewEntry`, `previousScrapedAt`, `placeId`, `name`, `isMyPlace`, `category`, `address`, `roadAddress`, `latitude`, `longitude`, `visitorRating`, `visitorReviewCount`, `blogReviewCount`, `totalResults`, `url`, `searchUrl`, `scrapedAt`.

Every row also has `schemaVersion` (currently 1) and `source` (`naver-place`). Every row has every field. A value Naver did not provide is `null` (Naver hides the star rating for many places, so `visitorRating` is often `null`). Numbers are numbers.

#### Run summary

The key-value store record `RUN_SUMMARY` has one entry per keyword with `status` (`completed`, `partial`, `failed`, `budgetLimited`, `notStarted`), `stopReason` (`maxRank`, `allMyPlacesFound`, `endOfResults`, `noResults`, `requestFailed`, `chargeLimit`), `ranksScanned` and `pagesCharged`.

### Which ranking is this?

- Ranks are the **organic order of Naver's Place list** (the list behind Naver Map search and the "플레이스" section of Naver search on PC). Paid ad slots (플레이스 광고) are not part of this list. For a restaurant keyword we checked that the general list and the restaurant list return the same order.
- **Location:** keywords with an area name (`강남 맛집`, `성수 카페`) gave the same top 50 with no location, Gangnam Station coordinates and Busan City Hall coordinates in our tests. Keywords without an area name depend fully on location: `카페` with no location returns places around Seoul City Hall (Jung-gu, Jongno-gu); with Gangnam coordinates, Gangnam and Seocho places; with Busan coordinates, Busan places. Set `searchLocation` for such keywords.
- Ranks on a customer's phone can still differ slightly (personalisation, logged-in history, app filters). Use this Actor for consistent day-to-day tracking under the same conditions.
- Naver lists about 300 places per keyword, so `maxRank` stops at 300.

### Pricing and cost control

Pay per event: you are charged per 50 ranks actually scanned for each keyword (`places-scanned`). A keyword with `maxRank` 50 is one event, `maxRank` 300 is up to six. Without `includeTopList`, a keyword stops as soon as all your places are found, so you often pay less than `maxRank` suggests. Keywords that fail are not charged. If you set **Maximum cost per run**, the Actor stops before going over it, keeps everything scanned so far and marks the rest `budgetLimited`.

### Tips

- **Schedule it daily** (Apify Schedules) with the same input: `rankChange` then shows the day-to-day movement.
- **One history store per client:** set `historyStoreName`, e.g. `client-a-place-ranks`.
- History is kept per keyword **and** location: changing `searchLocation` starts a new history.
- Only completed scans become the next baseline, so a failed run never wipes your comparison.

### Data and limits

Public Naver Map listing data only (business names, categories, addresses, rating and review counts). No login, no reviewer or personal data. Not affiliated with Naver.

# Actor input Schema

## `keywords` (type: `array`):

Required. Naver Map search keywords, one per line, typed as a Korean customer would. Examples: 강남 맛집 (Gangnam restaurants), 성수 카페 (Seongsu cafes), 홍대 미용실 (Hongdae hair salons). A pasted map.naver.com search URL also works.

## `myPlaces` (type: `array`):

Your places to track, one per line: the Naver place ID or a link to the place. Examples: 1501340733, https://map.naver.com/p/entry/place/1501340733, https://m.place.naver.com/restaurant/1501340733/home, https://naver.me/xxxxxxx. For each keyword you get one row (type = myPlaceRank) with the rank and the change since the last run. Leave empty to get only the ranked list.

## `myPlaceNames` (type: `array`):

Optional. Match places by name when you do not know the ID, one per line, e.g. 생활생돈 or 스타벅스 강남역점. Spaces and letter case are ignored and a name also matches longer names that start with it (생활생돈 matches 생활생돈 선릉본점). The row shows the best-ranked match and nameMatches = how many places matched. IDs are more reliable: many businesses share a name.

## `maxRank` (type: `integer`):

How deep to scan each keyword: ranks 1 to maxRank, 50 per request. Default 50, maximum 300 (Naver Map lists about 300 places per keyword). You are charged per 50 ranks actually scanned, so maxRank 300 costs up to 6 times maxRank 50. Without the full list, a keyword stops as soon as all your places are found.

## `searchLocation` (type: `string`):

Optional. Where the customer is searching from, as "latitude,longitude", e.g. 37.4979,127.0276 (Gangnam Station). Naver ranks nearby places higher, so ranks can differ by location, especially for keywords without an area name (카페, 미용실). Leave empty to use Naver's default (no location sent). Rank history is kept separately per location.

## `includeTopList` (type: `boolean`):

Also output every ranked place (type = rankedPlace) up to maxRank with name, category, address, rating, review counts and rank change. Turned on automatically when no places are given.

## `compareWithLastRun` (type: `boolean`):

Add previousRank, rankChange (positive = moved up) and isNewEntry, compared with the last completed run of the same keyword and location. The previous ranks are kept in a named key-value store in your account, so scheduled daily runs line up automatically.

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

Named key-value store that keeps the last ranks per keyword and location. Use a different name for each separate tracking project (for example one per client).

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

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

## Actor input object example

```json
{
  "keywords": [
    "강남 맛집"
  ],
  "myPlaces": [
    "1501340733"
  ],
  "maxRank": 50,
  "searchLocation": "37.4979,127.0276",
  "includeTopList": false,
  "compareWithLastRun": true,
  "historyStoreName": "naver-place-rank-history",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per keyword and tracked place (type = myPlaceRank) and, if includeTopList is on, every ranked place (type = rankedPlace).

## `runSummary` (type: `string`):

Per-keyword status (completed, partial, failed, budgetLimited, notStarted), stopReason, ranksScanned and pages charged. Failed keywords are not charged.

# 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 = {
    "keywords": [
        "강남 맛집"
    ],
    "myPlaces": [
        "1501340733"
    ],
    "maxRank": 50,
    "searchLocation": "37.4979,127.0276",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("magenta_courser/naver-place-rank-tracker").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 = {
    "keywords": ["강남 맛집"],
    "myPlaces": ["1501340733"],
    "maxRank": 50,
    "searchLocation": "37.4979,127.0276",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("magenta_courser/naver-place-rank-tracker").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 '{
  "keywords": [
    "강남 맛집"
  ],
  "myPlaces": [
    "1501340733"
  ],
  "maxRank": 50,
  "searchLocation": "37.4979,127.0276",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call magenta_courser/naver-place-rank-tracker --silent --output-dataset

```

## MCP server setup

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

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/Wr04DgOIx0PGRRbmJ/builds/r4xjHaYr08893ftZn/openapi.json
