# Naver Blog, Cafe & News Search Scraper (네이버 블로그·카페) (`magenta_courser/naver-blog-cafe-search-scraper`) Actor

Enter keywords and get Naver Blog, Naver Cafe and Naver News search results as flat rows: title, URL, snippet, blog/cafe/publisher name and publish time. Date filters and an only-new-since-last-run monitoring mode. No API key, no shared daily quota. 네이버 블로그·카페 크롤링.

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

## Pricing

from $0.80 / 1,000 posts

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, Cafe & News Search Scraper

Enter **keywords** and get **Naver Blog, Naver Cafe and Naver News search results** as flat JSON rows: title, URL, snippet, blog / cafe / publisher name and publish time in Korea time. Filter by date, or switch on **monitoring mode** to get only the posts that are new since your last run. Naver Blog and Naver Cafe are where people in South Korea write about products, places and daily life, so this is a practical way to watch **new Korean blog, cafe and news mentions of a brand, product or place**. **No login and no Naver API key** — the Actor reads Naver's public search pages directly.

What this is not: the rows are search-result titles and snippets of blog posts, cafe posts and articles. They are **not verified buyer reviews** — blog results include sponsored and seeded posts, and a mention is not a purchase. If you only need blog/cafe/news search results and are happy to manage your own key and daily quota, Naver's official [Search API](https://developers.naver.com/docs/serviceapi/search/blog/blog.md) is a free alternative. This Actor is for when you want no key, Korea-time dates, date ranges, one flat table across three tabs and a ready-made "only new since last run" monitor.

### What you get

One row per post or article, in Naver's order (newest first by default):

- Source (`blog`, `cafe` or `news`) and the keyword that found it
- Rank in Naver's result list for that keyword and source
- Title and post URL (cafe URLs cleaned to `cafe.naver.com/<cafe>/<articleId>`)
- The snippet Naver shows in search results
- Blog name, cafe name or news publisher (`channelName`)
- Publish time as ISO 8601 with `+09:00`, plus how exact it is (`minute`, `hour`, `day`, `week` or `month`)
- Thumbnail URL and a sponsored-result flag (blog only)
- `schemaVersion`, `source` (`naver`) and a stable `id` for joining runs

Extra features:

- **Date range** (`dateFrom`, `dateTo`) for all three sources
- **Only new posts since the last run** (`onlyNewSinceLastRun`) — the Actor remembers what it already returned for each search, so a scheduled run gives you mostly just the new mentions (see [How monitoring works](#how-monitoring-works) for what it can miss)
- **Run summary** — a free `RUN_SUMMARY` record says for each keyword and source whether the search `completed`, was `partial`, was `budgetLimited` or `failed`, how many rows it returned and why it stopped
- Newest-first or relevance order

No browser is used, so runs are quick: in our test run 2,700 rows took about 4 minutes.

**Not collected:** full post text, author nicknames and profile pictures. Only what Naver shows publicly in the search result list.

### Use cases

- **Brand and product monitoring in Korea** — schedule a daily run with your brand name and get each day's new blog and cafe mentions
- **Influencer and seeding campaign tracking** — check which blog reviews went live for a product name and when
- **Competitor watch** — follow what Korean consumers write about competing brands, stores or launches
- **Mention analysis with AI** — feed titles and snippets into an LLM to summarize topics, complaints and questions (remember: snippets of public posts, including sponsored ones, not verified reviews)
- **PR and news tracking** — add the `news` source to follow Korean media coverage of a keyword
- **AI agents** — one keyword in, flat English-named rows out

### Input

| Field | Description |
|---|---|
| `keywords` | **Required.** Search keywords, one per line. Korean keywords return the most results: `올리브영 선크림` (Olive Young sunscreen), `제주 카페` (Jeju cafe), or your brand or product name. |
| `sources` | Any of `blog`, `cafe`, `news`. Default `["blog", "cafe"]`. |
| `maxResultsPerKeyword` | Maximum rows **per keyword and per source** (1–1000). Default 100. |
| `sort` | `recent` (newest first, default) or `relevance`. |
| `dateFrom` | Optional. Only posts published on or after this date, Korea time, `YYYY-MM-DD`. |
| `dateTo` | Optional. Only posts published on or before this date, Korea time, `YYYY-MM-DD`. Defaults to today. |
| `onlyNewSinceLastRun` | Monitoring mode. Return only posts not returned in earlier runs of the same search. Default `false`. |
| `lookbackHours` | Monitoring mode only. How many hours before the previous complete run each run re-checks, to catch posts Naver lists late. 0–720, default `48`. |
| `historyStoreName` | Name of the key-value store that remembers returned posts in monitoring mode. Use one name per monitor. Default `naver-search-monitor-history`. |
| `proxyConfiguration` | Proxy settings. The default Apify datacenter proxy works. |

One-off search for a period:

```json
{
  "keywords": ["올리브영 선크림"],
  "sources": ["blog", "cafe", "news"],
  "maxResultsPerKeyword": 300,
  "sort": "recent",
  "dateFrom": "2026-09-01",
  "dateTo": "2026-09-15"
}
```

Daily monitor (run it on a schedule):

```json
{
  "keywords": ["your brand", "your product name"],
  "sources": ["blog", "cafe"],
  "maxResultsPerKeyword": 500,
  "onlyNewSinceLastRun": true,
  "historyStoreName": "my-brand-monitor"
}
```

### Output

Rows from real runs (shown in the current output format). A blog post found minutes after it was published:

```json
{
  "schemaVersion": 1,
  "source": "naver",
  "type": "blog",
  "id": "blog:blog.naver.com/mjmj4990/224428709437",
  "keyword": "제주 카페",
  "rank": 1,
  "title": "So what? We hot We young",
  "url": "https://blog.naver.com/mjmj4990/224428709437",
  "summary": "너무 배고프니 인스타에서 보고 궁금했던 브런치 카페로 먼저 달려왔어요.. 감성을 위해 이날 꽤 더웠는데도..... 카페갔다가.. 오랜만에 프리윌피자를 먹어주었어요 치즈스틱(?)도 먹었는데 겁나 야르한 맛ㅠㅠㅠ 진짜 무리해서 저녁먹고... ㅜ-ㅜ 그리고 서히가 자꾸 그릇에 덜어줌ㅋㅋ 연하력 먼데~~❤️❤️ 2차로 카페도 갔는데 예쁜이들이 먼저 다 잘라줌ㅠㅠ ㅋㅋㅋㅋ...",
  "channelName": "oon__mm",
  "publishedAt": "2026-10-01T21:56:00+09:00",
  "publishedAtPrecision": "minute",
  "thumbnailUrl": "https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyNjEwMDFfNDUg%2FMDAxNzkwODU1MTcxODAw.L_xMuCInm_9aqzbBNXooeJwdaVCRcQks8nRgi_6T9owg.r_NzxM4MaI3_ixUNz7QeNgGWP45OTqlRLEaF5T1RjWMg.JPEG%2FIMG%25A3%25DF5692.jpg%23900x1200&type=ff192_192",
  "isAd": false,
  "scrapedAt": "2026-10-01T12:59:32.704Z"
}
```

A cafe post with a date filter (2026-09-01 to 2026-09-15). This post has no image, so `thumbnailUrl` is `null`:

```json
{
  "schemaVersion": 1,
  "source": "naver",
  "type": "cafe",
  "id": "cafe:cafe.naver.com/imsanbu/79951279",
  "keyword": "올리브영 선크림",
  "rank": 5,
  "title": "임산부 선크림 뭐 쓰세요?",
  "url": "https://cafe.naver.com/imsanbu/79951279",
  "summary": "검색 해봤는데 특정브랜드만 계속 추천하길래 좀 의심들어서 댓글 활동 보니 광고같더라구요 가입만 하고 그 브랜드만 자꾸 추천하는.. 광고는 그냥 가시구요... 뭐가 좋을까요?",
  "channelName": "맘스홀릭 베이비 (360만 엄마들의 임신, 육아 지식in 카페)",
  "publishedAt": "2026-09-01T00:00:00+09:00",
  "publishedAtPrecision": "day",
  "thumbnailUrl": null,
  "isAd": null,
  "scrapedAt": "2026-10-01T13:02:54.580Z"
}
```

A news article from the same run. Naver showed it as "2주 전" (2 weeks ago), so the precision is `week` and `publishedAt` is the latest possible day:

```json
{
  "schemaVersion": 1,
  "source": "naver",
  "type": "news",
  "id": "news:www.gokorea.kr/news/articleView.html?idxno=878300",
  "keyword": "올리브영 선크림",
  "rank": 1,
  "title": "누메 '슈퍼 내추럴 슬립 선크림', 올리브영 입점… 카테고리 1위 기록",
  "url": "https://www.gokorea.kr/news/articleView.html?idxno=878300",
  "summary": "화장품 브랜드 누메(nume)가 '슈퍼 내추럴 슬립 선크림'을 올리브영에 입점시키며 오프라인 유통망을 넓혔다고 밝혔다. 누메에 따르면 '슈퍼 내추럴 슬립 선크림'은 올리브영 입점 직후 판매량이 증가해 해당 품목 카테고리 1위에 올랐다. 기존 온라인 중심 판매 제품으로, 올리브영 입고 후 초기 배정 물량이...",
  "channelName": "공감신문",
  "publishedAt": "2026-09-17T00:00:00+09:00",
  "publishedAtPrecision": "week",
  "thumbnailUrl": "https://search.pstatic.net/common/?src=https%3A%2F%2Fimgnews.pstatic.net%2Fimage%2Forigin%2F5264%2F2026%2F09%2F14%2F697156.jpg&type=ofullfill208_208_dominantcolor&expire=2&refresh=true",
  "isAd": null,
  "scrapedAt": "2026-10-01T13:02:54.350Z"
}
```

Export the dataset as JSON, CSV or Excel, or read it through the Apify API. Text values (titles, snippets, names) are in Korean as published; field names are English. Every row has every field; a value Naver does not show (or the Actor does not detect, like `isAd` for cafe and news) is `null`, never missing or `0`.

#### Run summary (free)

Each run also writes a `RUN_SUMMARY` record to its key-value store (the **Run summary** output tab), one entry per keyword and source. It is not a dataset row and is not charged.

```json
{
  "keyword": "올리브영 선크림",
  "source": "blog",
  "status": "completed",
  "stopReason": "reachedHistoryWindow",
  "returnedCount": 12,
  "pagesFetched": 2,
  "skippedAlreadyReturned": 31,
  "skippedBeforeWindow": 5,
  "windowStart": "2026-09-29T21:00:00.000Z",
  "errorMessage": null
}
```

| `status` | Meaning |
|---|---|
| `completed` | Covered what you asked for: end of results, `maxResultsPerKeyword` reached (one-off searches), or in monitoring mode reached the previous run's window |
| `partial` | Stopped early and may have gaps: monitoring run hit `maxResultsPerKeyword` before reaching the previous run, Naver's ~1,000-result limit, or requests failed after some rows were saved |
| `budgetLimited` | Your maximum cost per run was reached; `returnedCount` is what was actually saved and charged. Searches not started show `returnedCount: 0` |
| `failed` | Nothing reliable could be read (blocked requests, or Naver changed its page layout — the run then fails so you notice) |

`stopReason` is one of `endOfResults`, `noResults`, `maxResultsReached`, `reachedHistoryWindow`, `naverResultLimit`, `repeatedResults`, `chargeLimitReached`, `requestsFailed`, `layoutChanged`.

### How monitoring works

With `onlyNewSinceLastRun: true` the Actor keeps one history record per **keyword + source + sort + date range** in the key-value store `historyStoreName`. Runs with a different date range or sort never share history.

- A post is returned once. Only rows that were actually saved (and charged) are written to history, so if your cost limit stops a run, the posts it could not save come back in the next run.
- With `sort: recent`, each run reads newest first and checks every post dated after *(start of the previous complete run − `lookbackHours`)* against the history, so posts that show up in Naver search a little late are still returned. It stops after 5 posts in a row that are dated before that window.
- If a run stops because of `maxResultsPerKeyword`, the cost limit or errors before reaching the previous run (`partial` / `budgetLimited`), the window is **not** moved forward, so the next run continues back to it.
- The first run sets the starting point: older posts that the first run skipped because of `maxResultsPerKeyword` are not returned later.

**Not a complete-coverage guarantee.** You can still miss posts that Naver indexes more than `lookbackHours` after their publish time, posts Naver never shows in search, posts beyond Naver's ~1,000-result limit, and posts when more new ones appear between runs than `maxResultsPerKeyword` allows for several runs in a row. Two runs of the same monitor at the same time keep both runs' history, but may return some posts twice. With `sort: relevance` the Actor only skips posts it already returned; it cannot stop at the previous run.

### Tips

- **Monitoring:** turn on `onlyNewSinceLastRun`, keep `sort` at `recent` and add a schedule in Apify Console. The first run returns the latest posts; later runs return posts not returned before, back to the previous run minus `lookbackHours`. Check `RUN_SUMMARY`: if a search is often `partial` with `maxResultsReached`, raise `maxResultsPerKeyword` or run more often.
- **One history store per monitor.** History is already separate per keyword, source, sort and date range. If you run two different monitors with the same searches (for example, one for a client report and one for yourself), give them different `historyStoreName` values so one does not hide posts from the other. Do not schedule the same monitor so that two runs overlap.
- **Match run frequency to keyword volume.** Each run returns at most `maxResultsPerKeyword` rows per keyword and source. Broad keywords can get many posts per day: in our test, `다이소` (Daiso) and `제주 카페` (Jeju cafe) each returned 300 blog posts covering only the last 2–3 hours, while `올리브영 선크림` returned 300 blog posts covering about 3 days and 300 cafe posts covering about a month. For broad keywords, run more often or use a more specific keyword.
- **Going further back:** Naver lists about 1,000 results per search. To collect more for one keyword, split the period with `dateFrom` / `dateTo` (for example, one run per week).
- **Cost control:** you pay per row returned. Searches with no results return no rows, and pages that only contain already-returned posts cost nothing. Set a maximum cost per run in the run options if you want a hard cap; the Actor stops when it is reached and marks the affected searches `budgetLimited` in `RUN_SUMMARY`.
- Use `url` as the unique key when combining runs. The same post can appear under two different keywords — once per keyword.

### Notes

- **Publish time precision.** Naver shows recent posts as "5분 전" (5 minutes ago), "3시간 전" (3 hours ago), "2주 전" (2 weeks ago) and older ones as a date. `publishedAt` is converted from that label and truncated to `publishedAtPrecision`. For `minute`, expect an error of a few minutes, because Naver caches search pages briefly. For `week` and `month`, `publishedAt` is the latest possible day, so a post can show a date slightly after your `dateTo` even though Naver placed it inside your range.
- **About 1,000 results per keyword, source and date range** — this is Naver's own limit; `maxResultsPerKeyword` is capped at 1,000.
- **Snippets only.** `summary` is the search-result snippet, not the full post. Full text, comments and author nicknames are not collected.
- **Private cafe posts** are not opened. Only the title and snippet that Naver shows publicly in search results are returned.
- `thumbnailUrl` is `null` when a post has no image (common for cafe posts).
- The blog tab also lists some external blogs (for example Tistory); they are returned with `type: "blog"` and their own URL.
- `isAd` marks sponsored blog results that Naver labels as ads; we have not seen labelled rows in newest-first results so far. Posts written for a sponsorship without an ad label are not detected. For cafe and news `isAd` is `null`.
- If Naver changes its search page layout, the Actor fails with a clear error message instead of returning empty results silently. Rows that could be read are still saved.
- This Actor collects only publicly visible search results. It does not log in and does not collect author nicknames, but `channelName` (blog or cafe name), titles and snippets are written by users and may contain personal details they chose to share; handle them accordingly.

# Actor input Schema

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

Required. Search keywords, one per line, exactly as you would type them into Naver. Korean keywords return the most results, e.g. 올리브영 선크림 (Olive Young sunscreen), 제주 카페 (Jeju cafe), or a brand or product name. Each keyword is searched separately in every selected source.

## `sources` (type: `array`):

Which Naver search tabs to read. Any of: blog (Naver Blog, also includes some external blogs such as Tistory), cafe (Naver Cafe community posts), news (Naver News articles). Default: \["blog", "cafe"].

## `maxResultsPerKeyword` (type: `integer`):

Maximum rows for each keyword in each source (1-1000). Default 100. Example: 2 keywords x 2 sources x 100 = up to 400 rows. Naver lists about 1,000 results per search, so to go further back split the period with dateFrom/dateTo and run once per period.

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

Order of results: recent (newest first, default; required for monitoring to stop at the previous run) or relevance (Naver's relevance ranking).

## `dateFrom` (type: `string`):

Optional. Only posts published on or after this date, Korea time, format YYYY-MM-DD, e.g. 2026-09-01. If only dateTo is set, dateFrom defaults to 2000-01-01.

## `dateTo` (type: `string`):

Optional. Only posts published on or before this date, Korea time, format YYYY-MM-DD, e.g. 2026-09-15. If only dateFrom is set, dateTo defaults to today.

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

Monitoring mode. true = return only posts this Actor has not returned before for the same search (same keyword, source, sort and date range; remembered in the history store below). The first run returns the latest posts as usual. Use with sort recent and a schedule (e.g. daily). Best effort, not a guarantee of complete coverage: posts that Naver indexes later than lookbackHours after their publish time, or more new posts than maxResultsPerKeyword between runs, can be missed. Default false.

## `lookbackHours` (type: `integer`):

Only used when onlyNewSinceLastRun is true. Each run re-checks posts dated up to this many hours before the previous complete run started, so posts that appear in Naver search late are still returned once. Larger = fewer missed late posts, a few more pages read (pages are not charged, only returned rows). 0-720, default 48.

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

Only used when onlyNewSinceLastRun is true. Name of the key-value store in your Apify account that remembers already returned posts. Inside it, every keyword + source + sort + date range has its own record, so different searches never share history. Use a separate name for each separate monitor, e.g. brand-monitor-daily, and do not run the same monitor twice at the same time (it can return some posts twice). Letters, digits and hyphens only, max 63 characters.

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

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

## Actor input object example

```json
{
  "keywords": [
    "올리브영 선크림"
  ],
  "sources": [
    "blog",
    "cafe"
  ],
  "maxResultsPerKeyword": 100,
  "sort": "recent",
  "onlyNewSinceLastRun": false,
  "lookbackHours": 48,
  "historyStoreName": "naver-search-monitor-history",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `posts` (type: `string`):

All scraped blog posts, cafe posts and news articles as dataset items.

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

Free (not charged). One entry per keyword and source: status (completed / partial / budgetLimited / failed), returnedCount, stopReason, pages read, posts skipped as already returned, and the monitoring window used.

# 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": [
        "올리브영 선크림"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("magenta_courser/naver-blog-cafe-search-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 = {
    "keywords": ["올리브영 선크림"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("magenta_courser/naver-blog-cafe-search-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 '{
  "keywords": [
    "올리브영 선크림"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call magenta_courser/naver-blog-cafe-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,magenta_courser/naver-blog-cafe-search-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/e1AoQWpDRo1eleTQn/builds/hneuorhF5lhtyhcj6/openapi.json
