# Olive Young Global Bestsellers & Price Tracker (`cemented_jest/oliveyoung-global-bestsellers`) Actor

Collect K-beauty bestseller rankings, USD prices, stock indicators and review counts. Compare complete snapshots across runs.

- **URL**: https://apify.com/cemented_jest/oliveyoung-global-bestsellers.md
- **Developed by:** [CRNO](https://apify.com/cemented_jest) (community)
- **Categories:** E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 product 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

## Olive Young Global Bestsellers & Price Tracker

Collect Olive Young Global's bestseller rankings as structured product data. Get names, brands, USD prices, price ranges, discounts, stock indicators, ratings and review counts. Reuse the same history store across scheduled runs to compare prices and ranks.

The Actor uses public HTTP responses. No browser or Olive Young login is required by the implementation. Direct access can still be blocked in some environments; optional proxy configuration is available.

### Quick start

Use the default input to collect the overall bestseller ranking, up to 100 products:

```json
{
  "categoryIds": ["all"],
  "maxItemsPerCategory": 100,
  "trackChanges": true,
  "historyStoreName": "oliveyoung-global-history-v1",
  "proxyConfiguration": { "useApifyProxy": false }
}
```

For overall, skincare and suncare rankings:

```json
{
  "categoryIds": ["all", "1000000008", "1000000011"],
  "maxItemsPerCategory": 100,
  "trackChanges": true,
  "historyStoreName": "my-kbeauty-tracker"
}
```

The current site's category links are saved in the `CATEGORIES` key-value store record. The Actor rejects unknown category IDs. A recognized category can still return no bestseller data; that category is reported as failed, not as a successful empty result. Category availability may change.

### Input

| Field | Default | Purpose |
|---|---|---|
| `categoryIds` | `["all"]` | Up to 10 overall/category rankings; duplicates removed |
| `maxItemsPerCategory` | `100` | Save the top 1–100 products of each category |
| `trackChanges` | `true` | Read and save complete comparison snapshots |
| `historyStoreName` | `oliveyoung-global-history-v1` | Named store in your account; reuse for comparisons |
| `proxyConfiguration` | Direct | Explicitly enable an Apify or custom proxy if needed |

A lower item limit reduces the output, not the size of the source response. A product appearing in two categories produces two results because it has two separate ranks.

### Output

Products go to the default dataset, exportable through Apify as JSON, CSV or Excel. Example values below illustrate the format; live prices change.

```json
{
  "schemaVersion": 1,
  "productId": "GA240824996",
  "name": "MEDIHEAL Essential Mask Sheet 10ea Set (+1ea) (OY-Exclusive) (7 Options)",
  "brand": "MEDIHEAL",
  "brandKo": "메디힐",
  "categoryId": "all",
  "categoryName": "All",
  "rank": 1,
  "market": "global",
  "currency": "USD",
  "price": 13.5,
  "listPrice": 27,
  "minPrice": 13.5,
  "maxPrice": 13.5,
  "discountPercent": 50,
  "stockQuantity": 2408,
  "sourceStockQuantity": "2408",
  "dataWarnings": [],
  "isSoldOut": false,
  "rating": 4.9,
  "reviewCount": 8349,
  "productUrl": "https://global.oliveyoung.com/product/detail?prdtNo=GA240824996",
  "imageUrl": "https://cdn-image.oliveyoung.com/prdtImg/1219/4d15e768-500a-4a02-a1a4-ddd8bab694e7.png",
  "scrapedAt": "2026-11-05T05:30:00.000Z",
  "comparisonStatus": "noBaseline",
  "previousRunId": null,
  "previousScrapedAt": null,
  "previousRank": null,
  "rankChange": null,
  "previousPrice": null,
  "priceChange": null,
  "priceChangePercent": null,
  "stockChange": null,
  "isNewEntry": null,
  "runId": "example-run"
}
```

- `rankChange`: positive means moving up, e.g. rank 5 → 2 returns `3`.
- `priceChange`: current price minus previous price. Negative means cheaper.
- `priceChangePercent`: percentage change relative to the previous price.
- `stockChange`: current displayed stock minus previous displayed stock, when both are valid nonnegative numbers.
- `isNewEntry`: whether the product was absent from the previous comparable returned ranking. This does not mean the product itself is newly launched.
- `stockQuantity`: site-provided indicator, not independently verified inventory. Negative source values become `null`; `sourceStockQuantity` and `dataWarnings` preserve the reason.
- Missing optional values remain `null`. Prices are global USD listing prices, excluding shipping, tax and personalized discounts. `price` is the listing sale amount, not an individually selected variant price.

### Change tracking and scheduling

Schedule the Actor daily or hourly in Apify and keep `historyStoreName` unchanged. The first complete run establishes a baseline; a later run compares against the latest complete snapshot that started earlier. Categories and storefront/delivery context use separate comparison scopes.

Comparison statuses:

| Value | Meaning |
|---|---|
| `noBaseline` | First comparable run; change fields are null |
| `compared` | Comparable previous snapshot available |
| `baselineSmaller` | Previous response covered fewer products; unmatched products get `isNewEntry: null` |
| `incompleteRanking` | Response shrank by at least 5 products and below 80% of the prior count; comparisons are suppressed |
| `disabled` | Change tracking turned off |

Snapshots are written only after all products in that category are saved successfully. An item limit, budget limit, invalid data, failed dataset write or suspicious count drop leaves the previous baseline intact. A partial run can still provide product rows; inspect `SUMMARY`.

Snapshots are immutable, so concurrent runs cannot overwrite one another's history. Runs do not report disappeared products as deleted or sold out. If a genuinely smaller ranking repeatedly fails the size check, choose a new history store name to establish a fresh baseline after reviewing the source.

History is retained without automatic deletion and consumes storage over time. Avoid unnecessary high-frequency schedules. Automatic run resume is not implemented: a resurrected run with existing dataset rows fails before adding duplicates; start a fresh run instead.

### Diagnostics

The uncharged `SUMMARY` record includes per-category status, fetched/saved counts, baseline handling, errors, duration, request count, retry count and uncompressed response bytes. `CATEGORIES` lists category IDs discovered from the current site.

No data is emitted for a challenge page, invalid JSON, empty ranking, duplicate product ID or missing required product ID/name/price. HTTP 401/403/418/429 stops that request without repeated retries. Temporary network/5xx errors receive at most two retries. A run with every category failed is marked failed; mixed outcomes are marked `partial` in `SUMMARY`.

### Scope

This version covers global bestseller rankings and their product-card fields. It does not collect Korean-store prices, review text, full product descriptions, ingredients, individual SKU prices or a complete store catalog. It does not infer revenue or units sold from undocumented site fields. It is an independent tool, not affiliated with Olive Young.

### Pricing

**$2 per 1,000 saved product/category rows** ($0.002 per row), charged through the single **`product-scraped`** event. A product appearing in two categories counts as two rows. There is no subscription or startup fee.

There is no additional automatic dataset-item charge. Failed rows and `SUMMARY`/`CATEGORIES` records are not charged. Standard platform usage is included in the event price; no proxy is needed with the default configuration. The Actor respects the run's maximum charge limit.

# Actor input Schema

## `categoryIds` (type: `array`):

Use all for the overall ranking, or category IDs from Olive Young Global category URLs. Up to 10 categories; duplicates are removed. One result per product per category.

## `maxItemsPerCategory` (type: `integer`):

Up to 100. A lower limit reduces output, not the size of the site's response. Truncated runs do not advance the comparison baseline.

## `trackChanges` (type: `boolean`):

Compare with the latest complete snapshot in your named history store. First run creates a baseline. No removed-product claims are made.

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

Use the same name across scheduled runs. Use a different name to start fresh. Complete snapshots are retained; history storage grows over time.

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

Direct connection by default. If your Apify run is blocked, you can explicitly enable an Apify proxy; proxy usage may cost extra.

## Actor input object example

```json
{
  "categoryIds": [
    "all"
  ],
  "maxItemsPerCategory": 100,
  "trackChanges": true,
  "historyStoreName": "oliveyoung-global-history-v1",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `products` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {
    "categoryIds": [
        "all"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cemented_jest/oliveyoung-global-bestsellers").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 = { "categoryIds": ["all"] }

# Run the Actor and wait for it to finish
run = client.actor("cemented_jest/oliveyoung-global-bestsellers").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 '{
  "categoryIds": [
    "all"
  ]
}' |
apify call cemented_jest/oliveyoung-global-bestsellers --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cemented_jest/oliveyoung-global-bestsellers"
        }
    }
}
```

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/AjIenDvVLIY5BC4Yx/builds/SdhKd7je6pSbifeDN/openapi.json
