# Naver Scraper (`mlg14/naver-scraper`) Actor

Collect public Naver shopping search products, prices, stores, ratings, delivery details, and category IDs by keyword.

- **URL**: https://apify.com/mlg14/naver-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.59 / 1,000 results

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 Scraper

Scrape public Naver shopping results by keyword and export product names, prices, stores, ratings, delivery details, images, and category identifiers to JSON, CSV, or Excel. This Naver shopping data collector works from the product records shown in ordinary public search results, making it useful for focused Korean product discovery and repeated price snapshots.

The actor accepts several Korean or English search terms in one run. It writes one row per distinct listing per keyword, with direct product links and the search term that found each listing. It is designed for the small shopping selection shown in public general search. A broad catalogue crawl needs many well-chosen terms because each term exposes a limited selection.

### What data can you extract from Naver?

Each dataset row describes one product placement. Prices and delivery fees are amounts in Korean won; a blank value means the search listing did not provide it. The output keeps identifiers as strings so spreadsheet imports do not lose digits. The table below covers every output field.

| Field | Description | Example |
| --- | --- | --- |
| `keyword` | Search term that produced the row | `러닝화` |
| `searchUrl` | Public result page used for the row | `https://search.naver.com/search.naver?query=...` |
| `searchPosition` | Position within its shopping section, starting at one | `1` |
| `sourceSection` | Product section on the public page | `shopping` or `nstore` |
| `isSponsored` | Whether that placement is marked as sponsored | `true` |
| `nvMid` | Listing identifier used to remove duplicate placements | `60704830910` |
| `channelProductId` | Product identifier in the selling channel | `5446` |
| `catalogId` | Matched comparison catalog identifier, if supplied | `null` |
| `productName` | Product title without search highlight tags | `러닝화 ...` |
| `productUrl` | Direct desktop product URL | `https://.../product/detail.html?...` |
| `mobileProductUrl` | Direct mobile product URL, if supplied | `http://m...` |
| `imageUrl` | Primary product image | `https://shopping-phinf.pstatic.net/...jpg` |
| `images` | Image URL array from the listing | `[..., ...]` |
| `mallName` | Store name shown for the product | A store label |
| `mallId` | Store identifier, if supplied | `gtsglobal` |
| `mallUrl` | Store URL shown in the listing | `https://...` |
| `salePrice` | Positive regular price, if supplied | `89000` |
| `discountedSalePrice` | Discounted price, if supplied | `79000` |
| `price` | Selling price used for price filters | `79000` |
| `discountedRatio` | Discount percentage shown by the source | `11` |
| `priceCurrency` | Currency code | `KRW` |
| `averageReviewScore` | Average review rating | `4.9` |
| `totalReviewCount` | Total review count | `179` |
| `averageReviewScore6Month` | Recent average from the store section, when present | `4.78` |
| `totalReviewCount6Month` | Recent review count from the store section | `7517` |
| `purchaseCount` | Purchase count displayed with the listing | `3094` |
| `mallCount` | Number of selling stores when reported | `0` |
| `deliveryFee` | Displayed delivery fee in won | `0` |
| `freeDelivery` | Whether the displayed fee equals zero | `true` |
| `fastDeliveryType` | Source delivery-speed label | `NONE` |
| `lCatId` | Top-level category identifier | `50000001` |
| `mCatId` | Second-level category identifier | `50000174` |
| `sCatId` | Third-level category identifier | `50001466` |
| `ssCatId` | Fourth-level category identifier | `50003854` |
| `isBrandStore` | Source flag for a brand store | `false` |
| `isSmartStoreProduct` | Source flag for a store product | `false` |
| `isHotDeal` | Source hot-deal flag, if supplied | `false` |
| `productType` | Product type label from the source | `NORMAL` |
| `collectedAt` | UTC collection time for the run | `2026-09-27T20:34:46+00:00` |

The store section sometimes adds a six-month review average and count that the shopping section does not include. The shopping section can contain both sponsored and ordinary placements of the same listing. The actor removes repeated `nvMid` values within each keyword while preserving the first eligible placement. The same product may appear once for each of two different keywords; those separate rows preserve search context and let you measure where the product appeared.

`price` prefers `discountedSalePrice` when available, then falls back to a positive `salePrice`. A listed regular price of zero is treated as unavailable rather than as a free product. The discount percentage is the source value and is not recomputed from the two price fields. Use the price and timestamp together when comparing runs because listings, promotions, and stock can change.

### How to scrape Naver

1. Enter one or more shopping search terms in **Shopping keywords**. Korean terms usually reflect the names used in the local catalogue. Start with terms that describe different categories or product families.
2. Choose a total product limit if you need a fixed dataset size. Set a price range only when you want the actor to discard results outside that range.
3. Decide whether to include sponsored placements. Leaving them on reflects the complete public selection; turning them off keeps eligible ordinary placements.
4. Run the actor. It checks the public general search page for each term and records the shopping and store product collections shown there.
5. Open the dataset and download JSON, CSV, or Excel. Use `keyword`, `nvMid`, and `collectedAt` to compare later snapshots.

For a first run, use several unrelated terms rather than expecting a large result from one broad term. A focused page may show fewer than ten distinct products. More terms are the practical way to collect more products from this public surface.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `keywords` | String array | Required | Independent shopping search terms; Korean is usually the most specific choice. |
| `maxProductsPerKeyword` | Integer | `12` | Maximum distinct products retained for each term. The public page may expose fewer. |
| `maxItems` | Integer | `0` | Hard cap across all terms; zero removes the total cap. |
| `includeSponsored` | Boolean | `true` | Keep sponsored placements along with ordinary ones. |
| `minPrice` | Integer | Unset | Minimum displayed selling price in won. |
| `maxPrice` | Integer | Unset | Maximum displayed selling price in won. |
| `proxyConfiguration` | Object | Enabled | Proxy settings for public search requests. |

A realistic product research input is:

```json
{
  "keywords": ["러닝화", "세탁세제", "선크림", "커피믹스"],
  "maxProductsPerKeyword": 12,
  "includeSponsored": true,
  "maxItems": 0
}
```

That input was used for the verification run and produced 35 rows. The counts for an individual term can change between runs. The maximum is an upper bound, never a promise that every term contains twelve available products. If you specify a price range, filtering happens after the public search page is read; it cannot make the source return additional candidates beyond the visible selection.

### Output example

The following values come from a product returned by the verification run. The example shows a useful subset of its fields; the dataset row also includes the remaining fields from the table above.

```json
{
  "keyword": "러닝화",
  "sourceSection": "shopping",
  "searchPosition": 1,
  "isSponsored": true,
  "nvMid": "60704830910",
  "channelProductId": "5446",
  "catalogId": null,
  "productName": "밸롭 글라이드 러닝화 오프화이트 코리아에디션",
  "productUrl": "https://ballop.co.kr/product/detail.html?product_no=5446&cate_no=1287&display_group=1&utm_source=naver&utm_medium=shopping&utm_campaign=knowledge_shopping&mkt_in=Y&ghost_mall_id=naver&ref=naver_open",
  "imageUrl": "https://shopping-phinf.pstatic.net/main_6070483/60704830910.jpg",
  "mallName": "밸롭본사공식몰",
  "salePrice": null,
  "discountedSalePrice": 79000,
  "price": 79000,
  "priceCurrency": "KRW",
  "averageReviewScore": 4.9,
  "totalReviewCount": 179,
  "deliveryFee": 0,
  "freeDelivery": true,
  "lCatId": "50000001",
  "mCatId": "50000174",
  "sCatId": "50001466",
  "ssCatId": "50003854"
}
```

The product URL is a direct selling-page link supplied by the search result; it may point to a store outside the main shopping domain. The image URL is useful for catalogue matching and visual review. For spreadsheet work, `price` is usually the best single price column, while `salePrice` and `discountedSalePrice` explain how that value was chosen.

### Use cases

- **Korean product discovery:** Collect a manageable set of visible listings for several product categories before deeper manual research. The keyword column makes it clear which query surfaced each listing.
- **Price snapshots:** Schedule the same terms at a regular interval, then compare `nvMid`, `price`, and `collectedAt` to detect changes. Check the direct product page before acting on a large change because search prices can reflect temporary promotions.
- **Store comparison:** Group rows by `mallId` or `mallName` to see which stores appear for a family of terms. The output records the selling store shown for each listing, not a complete table of every merchant selling a catalog item.
- **Assortment tracking:** Compare product identifiers and categories between runs to see which listings enter or leave the public selection. A missing row can also reflect ranking changes, so do not equate absence with product removal.
- **Review-led screening:** Sort the visible products by `averageReviewScore` and `totalReviewCount` to find items worth inspecting. These are aggregate numbers shown with the product; individual reviews are not collected.
- **Delivery screening:** Filter returned rows for zero delivery fee or a source fast-delivery label. Delivery eligibility can depend on shopper location and checkout conditions, so treat listing labels as a search snapshot.

These workflows work best when the query list is stable. Reusing the same terms gives you a comparable sample. If you change terms, the row count and product mix may change for reasons unrelated to the products themselves.

### How much does it cost to scrape Naver?

The current result charge is **$0.001592 per product row**, or **$1.592 per 1,000 rows**. Charges follow delivered dataset items. Searches that return no retained product rows do not add result charges, although normal platform resource usage and account conditions may still apply. Check the run screen for the final amount.

| Delivered rows | Result charge |
| ---: | ---: |
| 35 | About $0.056 |
| 1,000 | $1.592 |
| 5,000 | $7.96 |

The 35-row example reflects the verified four-keyword input above. Reaching 1,000 or 5,000 rows takes many varied terms because the public page yields a small selection per term. A narrow price band, removal of sponsored placements, or repeated product appearances can lower the delivered count and its corresponding result charge.

Set `maxItems` when you need an upper bound on output volume. For example, `maxItems: 100` stops after one hundred delivered rows even if the input list contains more terms. If a term returns fewer records than `maxProductsPerKeyword`, the actor moves to the next term. A run also stops adding rows when its charged-result budget is reached.

### Tips for best results

Use terms that match how local shoppers describe products. Brand and model terms can be precise but may return fewer visible cards than a broad category. If the goal is a broader survey, split a category into meaningful terms such as product type, intended use, and size. Keep those terms in the dataset for later interpretation.

Start without a price filter to understand the visible result mix. Then add `minPrice` or `maxPrice` if the same term surfaces items outside the range you need. Prices are filtered against the chosen selling price in the output, after discounts where the listing provides one. The filter does not request a deeper price slice from the source. If you need midrange coverage, choose narrower terms describing that segment.

Use `includeSponsored: false` when ranking analysis should exclude placements explicitly marked as ads. The page may also show the same product in sponsored and ordinary positions. The actor keeps the eligible ordinary placement if it appears later. When sponsored listings are included, the first occurrence for a product is kept, so the `isSponsored` flag describes the retained placement.

Use `nvMid` for product-level matching and `channelProductId` when you need the identifier of a store listing. `catalogId` is often empty because the public card does not always expose a matched comparison catalog. Keep identifiers as strings in spreadsheets. Numeric import can round long values and make joins unreliable.

A new run fetches the current page for each keyword. Search rankings, promotions, delivery labels, review totals, and seller links can change. For monitoring, retain each run’s dataset or add its items to a dated table. Compare several snapshots before interpreting a short-lived difference.

### Limits

This actor collects the public product records embedded in ordinary Naver search results. In research, broad queries exposed about 7–12 distinct products each after duplicate placements were removed. The dedicated shopping search and catalog pages returned an HTTP 418 access restriction, including through a Korean residential proxy. The shopping JSON endpoint exposed by the page returned an empty list in the tested anonymous session. This actor therefore does not claim full shopping pagination, 10,000 products from one keyword, complete seller tables, product specifications, review text, or price history.

The public page may omit a section for a term or change its embedded data structure. Some fields are available in only one section. For example, six-month review figures appeared with certain store results, while comparison catalog identifiers were sparse. A blank field is an honest absence from the listing rather than a guessed value. Category fields are source identifiers; names and full category paths are not provided by this extraction.

Product links can point to external stores, and their availability is outside the search page’s control. Search price and delivery information can differ from checkout values. This actor collects publicly visible listing information only and does not log in or access private shopper data. If an input keyword returns no product state, the run logs that term and continues with the next; an entirely empty run fails visibly.

The verified input returned 35 rows with product names, links, prices, store names, images, identifiers, and timestamps present on every row. That is a check of one live run, not a guarantee of future result counts. For coverage beyond this public selection, use a source that has authorized access to deeper shopping results.

### Use with AI agents (MCP)

The input schema gives automated clients a small set of explicit parameters, and each dataset row has stable field names. Two useful requests are:

> Search `러닝화`, `세탁세제`, and `선크림`; collect up to twelve products per term; return product name, displayed price, store name, and direct URL.

> Run the same four Korean shopping terms every morning; compare `nvMid` and `price` with yesterday’s rows; list changed prices with both collection timestamps.

Keep an explicit product limit in automated workflows so cost and output size remain predictable. Ask for `keyword` and `collectedAt` alongside prices when comparing runs, because a price without its search context or time can be misleading.

### FAQ

**Is the data public, and may I use it?** The actor reads public search listing data without a login. Your use of the resulting data should comply with applicable terms, copyright rules, privacy law, and the rules of your own market. Avoid using listing links or store identifiers for unwanted contact.

**Do I need to configure a proxy?** The default input enables a proxy. Ordinary search results were available through the project’s remote network during verification. The dedicated shopping pages remained restricted, which is why the actor uses general search results.

**How fast is a run?** The verified four-keyword run finished within about a minute. Network retries, proxy changes, and the number of keywords can change runtime. The actor processes terms in order and stops once a total cap is reached.

**Can I schedule and monitor the same searches?** Yes. Save a fixed input and schedule repeat runs. Export each dataset with its collection time, then join rows by `nvMid` and keyword. A product disappearing from a shallow result selection does not prove it has been discontinued.

**Can I export to a spreadsheet?** Yes. Download CSV or Excel from the dataset. Use the overview columns for quick inspection and the full JSON rows for all optional fields. Import the ID fields as text to preserve their exact digits.

**Why is a field empty?** The public listing did not include it for that product or section. Some store results carry recent review figures, some products have no matched catalog ID, and some delivery details depend on the seller. The actor does not fill absent values with estimates.

**Why did a keyword produce fewer than twelve rows?** Twelve is the default stopping limit, while the public page often shows fewer distinct products. Sponsored and ordinary sections can repeat a listing. Price filters and `includeSponsored: false` can reduce the remaining rows further.

### Integrations

Use the actor’s dataset through its API or connect a finished run to webhooks, scheduling, automation flows, or a spreadsheet import. A practical monitoring workflow stores the JSON rows with the run date, joins on `nvMid` and keyword, and reports price differences only where both runs have a positive displayed price. Keep the direct product URL in the exported table so a reviewer can check a listing before making a purchasing or merchandising decision.

### Support

Open an issue on the Issues tab with the search term, run link, and the field you expected. We reply within 24 hours and add fields on request when they are available in the public result data.

# Actor input Schema

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

Search terms, preferably Korean. Each term is searched separately in public results.

## `maxProductsPerKeyword` (type: `integer`):

Stop after this many distinct products for each keyword. The public result page usually exposes only 7–12 products per term, even when this limit is higher.

## `maxItems` (type: `integer`):

Stop after this many product rows across all keywords. Set 0 for no total cap.

## `includeSponsored` (type: `boolean`):

Keep products labeled as sponsored in the public result page. Duplicate placements of the same product are removed within a keyword.

## `minPrice` (type: `integer`):

Keep products with a displayed selling price at or above this amount in Korean won.

## `maxPrice` (type: `integer`):

Keep products with a displayed selling price at or below this amount in Korean won.

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

Proxy settings for fetching public search result pages.

## Actor input object example

```json
{
  "keywords": [
    "러닝화",
    "선크림"
  ],
  "maxProductsPerKeyword": 12,
  "maxItems": 0,
  "includeSponsored": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped items in the default dataset.

# 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": [
        "러닝화",
        "선크림"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/naver-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": [
        "러닝화",
        "선크림",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/naver-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": [
    "러닝화",
    "선크림"
  ]
}' |
apify call mlg14/naver-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/naver-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/Yus1HjuVmV2fRdJ2q/builds/Bf1dtI1D7fb36Kmlw/openapi.json
