# Naver Blog Rank Tracker (네이버 블로그 순위) (`magenta_courser/naver-blog-rank-tracker`) Actor

Enter keywords and your Naver blog IDs or post URLs; get each blog's or post's rank in the Naver Blog search tab (top 30 by default, up to 1,000), with rank change since your last run. Optional full ranked list.

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

## Pricing

from $2.40 / 1,000 keyword 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 Blog Rank Tracker (네이버 블로그 순위)

Enter **keywords** and **your Naver blog IDs or post URLs**, get **the rank of your blog or post in the Naver Blog search tab** (블로그 탭) for each keyword, plus **the change since your last run**. Turn on `includeTopList` to also get the full ranked list (rank, title, post URL, blog ID, published date) for each keyword. No login, no Naver API key.

In Korea, blog "상위노출" (top placement) for a keyword is sold and paid for by rank: agencies, clinics, academies, restaurants and brand marketers check every day whether a post is still in the top 5, top 10 or top 30. This Actor does that check on a schedule and keeps the history for you.

### What you get

One row per **keyword × blog/post you track** (`type: "myBlogRank"`):

- `rank` of your best-ranked post for that keyword, or `null` when it is not within the ranks scanned
- `found`: `true`, `false` (not within `ranksScanned` of a finished search) or `null` (the search did not finish, so absence is unknown)
- `previousRank`, `rankChange` (positive = moved up), `isNewEntry`, `previousScrapedAt` compared with your last completed run
- `postUrl`, `postTitle`, `publishedAt` of the matching post, `postsInRanks` and `otherRanks` when the same blog has several posts in the list
- `ranksScanned`, `searchStatus`, `stopReason`, `searchUrl` (open it to see the same list) and `scrapedAt`

Optional ranked list (`type: "rankedPost"`, when `includeTopList` is on): `rank`, `title`, `postUrl`, `blogId`, `isExternalBlog`, `isMine`, `publishedAt`, `previousRank`, `rankChange`, `isNewEntry`.

All fields are flat English names, numbers are numbers, missing values are `null`.

### Input

| Field | What to enter |
|---|---|
| `keywords` (required) | Search keywords as a Korean user types them, e.g. `제주 카페`, `강남 맛집`, `피부과 추천`. A pasted Naver search URL also works. |
| `myBlogs` | Naver blog ID (`myblog123`), blog URL (`https://blog.naver.com/myblog123`) or post URL (`https://blog.naver.com/myblog123/223456789012`; `m.blog.naver.com` and `PostView.naver?blogId=…&logNo=…` links work too). A blog matches any of its posts; a post URL matches only that post. External blogs shown in the tab (e.g. Tistory) can be given by URL. |
| `maxRank` | How deep to look, default `30`, max `1000`. |
| `sort` | `relevance` (default, the order Naver shows in the blog tab) or `recent` (newest first). |
| `includeTopList` | Also return the full ranked list. Default `false`. |
| `compareWithLastRun` | Rank change vs the last completed run. Default `true`. |
| `historyStoreName` | Named key-value store that keeps the previous ranks. Default `naver-blog-rank-history`; use one name per tracking project. |

Example:

```json
{
  "keywords": ["제주 카페", "제주 디저트 카페"],
  "myBlogs": ["jejudo_official", "https://blog.naver.com/nana_80/224369123705"],
  "maxRank": 30
}
```

### Output example

A row from a real run (second run of the day, so the change fields are filled):

```json
{
  "schemaVersion": 1,
  "source": "naver-blog",
  "type": "myBlogRank",
  "keyword": "제주 카페",
  "sort": "relevance",
  "target": "jejudo_official",
  "targetType": "blog",
  "blogId": "jejudo_official",
  "found": true,
  "rank": 13,
  "previousRank": 12,
  "rankChange": -1,
  "isNewEntry": false,
  "previousScrapedAt": "2026-10-29T13:22:20.000Z",
  "postsInRanks": 1,
  "otherRanks": null,
  "postUrl": "https://blog.naver.com/jejudo_official/224377455358",
  "postTitle": "8월 제주도 여행에서 꼭 가봐야하는 가성비 디저트 카페 TOP 6",
  "publishedAt": "2026-08-13T00:00:00+09:00",
  "ranksScanned": 30,
  "maxRank": 30,
  "searchStatus": "completed",
  "stopReason": "maxRank",
  "searchUrl": "https://search.naver.com/search.naver?ssc=tab.blog.all&query=%EC%A0%9C%EC%A3%BC+%EC%B9%B4%ED%8E%98&sm=tab_opt&nso=so%3Ar%2Cp%3Aall",
  "scrapedAt": "2026-10-29T13:30:47.382Z"
}
```

The **Run summary** (key-value store record `RUN_SUMMARY`, free) lists every keyword with `status` (`completed`, `partial`, `failed`, `budgetLimited`, `notStarted`), `stopReason`, `ranksScanned`, pages read and how many checks were charged.

### How ranks are counted

- Ranks are positions in the **Naver Blog search tab** (`search.naver.com`, 블로그 tab), the same list a person sees when scrolling that tab, read without login, cookies or location.
- **Organic only**: results Naver labels as ads (파워컨텐츠) are skipped and do not take a rank.
- A post that Naver repeats further down the list keeps its first position.
- **Relevance order ends near rank 70.** In our tests (10 keywords, Oct 2026) Naver's blog tab served about 70 distinct posts in relevance order and then repeated earlier posts, so a relevance search stops there with `stopReason: "endOfResults"` and `found: false` means "not in the top ~70". With `sort: "recent"` the list goes deeper (tested to rank 300; Naver stops at about 1,000).
- Rank change compares with the last **completed** run of the same keyword and sort in your history store. `isNewEntry` is `true` only when the previous run scanned at least that deep, otherwise `null`.
- This Actor checks the blog tab only. Positions inside the integrated search page (통합검색 smart blocks) are not reported, because the blocks differ per keyword and have no single rank.

### Pricing

Pay per event:

- `keyword-check`: one per keyword whose search finished, covering up to 100 ranks actually read (relevance order always counts as one; a `recent` scan to rank 300 counts as three).
- `ranked-post`: one per row of the optional ranked list.
- Keywords that fail, stop early or have no blog results at all are **not charged**. Your rows for those keywords are still saved with `found: null`.
- When your **maximum cost per run** is reached, the Actor does not start further keywords, cuts the ranked list where the budget ends, and finishes with a message telling you to raise the limit.

### Scheduling tips

- Create a task with your keywords and blogs and schedule it daily. Keep the same `historyStoreName` so every run compares with the one before.
- Use a different `historyStoreName` per client or project. Stores created by other Actors cannot be opened; the run then continues without comparison and tells you to pick a new name.
- Ranks of popular keywords move during the day; schedule at the same hour for comparable numbers.

### Limits and notes

- Results can differ slightly from what a logged-in user sees in their browser (personalisation, location).
- `publishedAt` comes from Naver's display text: "3시간 전" is exact to the hour, "2주 전" or older posts are exact to the day only.
- The default Apify datacenter proxy has worked in all our tests (0 failed requests out of about 115, Oct 2026). Switch to `RESIDENTIAL` with country `KR` only if the log shows repeated errors.
- The Actor reads only public search results and returns blog IDs and post URLs, not author nicknames. Titles are written by bloggers and may contain whatever they chose to publish.
- If Naver changes the page layout, affected keywords fail with `stopReason: "layoutChanged"` (not charged); please report it in the Issues tab.

### Related Actors

- [Naver Blog, Cafe & News Search Scraper](https://apify.com/magenta_courser/naver-blog-cafe-search-scraper): full search results with snippets, date filters and a new-posts-only monitor.
- [Naver Shopping Keyword Rank Tracker](https://apify.com/magenta_courser/naver-shopping-rank-tracker): product ranks in Naver Shopping with the same `rankChange` fields.
- [Naver Place Search Scraper](https://apify.com/magenta_courser/naver-place-search-scraper): places and their order in Naver Map search.

# Actor input Schema

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

Required. Search keywords to check in the Naver Blog search tab (search.naver.com, 블로그 tab), one per line, typed as a Korean user would. You can also paste a Naver search URL; its query is used. Examples: 강남 맛집 (Gangnam restaurants), 제주 카페 (Jeju cafe), 올리브영 선크림. Each keyword is one search and one charged keyword check (per started 100 ranks); keywords that fail are free.

## `myBlogs` (type: `array`):

Your blogs or posts to find, one per line: a Naver blog ID (myblog123), a blog URL (https://blog.naver.com/myblog123) or a post URL (https://blog.naver.com/myblog123/223456789012; m.blog.naver.com and PostView links work too). A blog ID or blog URL matches any post of that blog (best rank wins); a post URL matches that post only. External blogs that appear in the tab, such as Tistory, can be given by URL. For each keyword you get one row per entry (type = myBlogRank). Leave empty to get only the ranked list.

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

How deep to look: ranks 1 to maxRank of the blog tab (30 per request). Default 30 (about what a person scrolls). Naver's blog tab stops at about 1,000 results, so 1,000 is the maximum. One keyword check covers up to 100 ranks actually read; deeper scans count one check per started 100 ranks (a recent-order scan to 300 = 3 checks). Relevance order ends near rank 70 on Naver, so it is always one check.

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

relevance = the order Naver shows by default in the blog tab (what rank tracking usually means). recent = newest first.

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

Also return every post in ranks 1 to maxRank (type = rankedPost: rank, title, post URL, blog ID, published date, rank change, isMine). Each row of this list is charged as one ranked-post event. Default false.

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

Add previousRank, rankChange (positive = moved up) and isNewEntry, compared with the last completed run of the same keyword and sort. The 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 sort. Use a different name for each separate tracking project (letters, digits and hyphens, max 63 characters).

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

The default Apify datacenter proxy works for Naver blog search. Switch to RESIDENTIAL with country KR only if the log shows repeated request errors.

## `maxRequests` (type: `integer`):

Optional. Upper limit on HTTP requests for the whole run, retries included. Default: twice the pages needed + 5. When it is used up the run stops and keeps what it saved.

## `maxRunMinutes` (type: `integer`):

Optional. The run stops sending new requests after this many minutes (default 60) and keeps what it saved. It also stops after 10 failed requests in a row.

## Actor input object example

```json
{
  "keywords": [
    "제주 카페"
  ],
  "myBlogs": [
    "jejudo_official"
  ],
  "maxRank": 30,
  "sort": "relevance",
  "includeTopList": false,
  "compareWithLastRun": true,
  "historyStoreName": "naver-blog-rank-history",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per keyword and blog/post you track (type = myBlogRank) and, if includeTopList is on, the ranked posts (type = rankedPost).

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

Free. Run status and one entry per keyword: status (completed, partial, failed, budgetLimited, notStarted), stopReason, ranksScanned, pages read and whether it was charged. Only completed keywords are 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": [
        "제주 카페"
    ],
    "myBlogs": [
        "jejudo_official"
    ],
    "maxRank": 30,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("magenta_courser/naver-blog-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": ["제주 카페"],
    "myBlogs": ["jejudo_official"],
    "maxRank": 30,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("magenta_courser/naver-blog-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": [
    "제주 카페"
  ],
  "myBlogs": [
    "jejudo_official"
  ],
  "maxRank": 30,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call magenta_courser/naver-blog-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-blog-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/COGgNjTUO6bBV7J2V/builds/73OK1UfbCfbkQHbpz/openapi.json
