# Hwahae K-beauty Rankings Scraper (Korea) (`superslowsloth/hwahae-rankings`) Actor

Scrape Hwahae (화해) K-beauty bestseller rankings: trending, category, skin type and age rankings with rank, rank change, NEW flag, product, brand, price in KRW, discount, rating and review count. Daily trending ranking for Korean beauty trend and competitor tracking.

- **URL**: https://apify.com/superslowsloth/hwahae-rankings.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 ranking row scrapeds

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

## Hwahae K-beauty Rankings Scraper (Korea)

Read the bestseller rankings of [Hwahae (화해)](https://www.hwahae.co.kr/rankings),
Korea's largest cosmetics review app, and get one flat row per ranked product:
the **rank**, how far it **moved** since the last ranking, whether it is **new**
to the list, the product and brand, the shop price in **KRW**, the discount, the
rating and the review count. Run it on a schedule and keep the dataset to get a
**daily K-beauty trend history**: what is climbing in skincare, what a 20s
shopper buys, which brand just entered the top 50.

No account, no API key, no login, no browser.

### What you can read

Hwahae has four product ranking themes, each with a tree of categories:

| Theme | Rows | What it ranks |
|---|---|---|
| `trending` (급상승) | 50 | Products rising fastest, by category. Rebuilt daily at 05:00 Korea time. |
| `category` (카테고리별) | 100 | Best products per category (skincare, cleansing, mask, sun care, makeup, body, hair, perfume, men ...). |
| `skin` (피부별) | 100 | Best products per skin type (dry, oily, normal, combination, sensitive, acne, atopic), then by category. |
| `age` (연령대별) | 100 | Best products per age band (10s, 20s, 30s, 40s+), then by category. |

Hwahae also has a brand ranking; it ranks brands, not products, and is not
supported.

### Input

| Field | Notes |
|---|---|
| `rankings` | One ranking per line. Default: `trending`. See below. |
| `maxItems` | Rows per ranking, best rank first (default 100, which is the whole ranking; trending holds 50). |
| `proxyConfiguration` | Apify datacenter proxy by default. |

#### Naming a ranking

Write a theme, or a path down the theme's category tree, steps joined with `>`:

```
trending
trending > 스킨케어
skin > 민감성 > 스킨케어
age > 20대
category > 남성
```

- A group means its **overall ranking** (`trending > 스킨케어` is the ranking of
  all of skincare). Names under the group go as deep as Hwahae's tree goes
  (`trending > 스킨케어 > 스킨/토너 > 수분`).
- The tree is Korean only. The top groups, skin types and age bands also take
  English names: `trending > skincare`, `skin > sensitive`, `age > 20s`,
  `category > men`. Case and spaces are ignored.
- `skin` and `age` on their own read Hwahae's default for that theme (what the
  site opens on: combination skin, 30s). The `ranking_name` column says which.
- A name that is not in the tree fails that ranking (no charge) and the log
  lists what the valid names are at that step.
- Instead of a path you can paste the ranking's page URL from hwahae.co.kr
  (`https://www.hwahae.co.kr/rankings?english_name=skin&theme_id=687`), or write
  the theme and its numeric id: `skin > 687`.
- A ranking asked for twice (by name and by id, say) is read once.

### Output

One row per position in a ranking. The same product appears once in each
ranking it is in, so the key of a row is `ranking_id` + `rank`.

| Field | Notes |
|---|---|
| `theme`, `theme_name`, `ranking_id`, `ranking_name`, `ranking_url` | Which ranking the row belongs to. `ranking_name` is the path in Hwahae's tree (Korean), e.g. `스킨케어 > 전체`. |
| `ranking_updated_at`, `ranking_size` | When Hwahae last rebuilt the ranking (as sent, Korea time, no offset) and how many rows it has. |
| `rank`, `rank_delta`, `is_rank_new` | Position from 1. `rank_delta` is places moved since the previous ranking: positive = up, negative = down, 0 = same. `is_rank_new` marks a product that was not in the previous ranking. |
| `product_id`, `product_name`, `product_url`, `image_url` | Hwahae's catalogue entry. |
| `brand_id`, `brand_name` | As Hwahae names the brand (Korean). |
| `price`, `currency`, `discount_rate`, `capacity` | The shop price in KRW of Hwahae's own offer for the product, the percent off as Hwahae states it (the reference price it is measured against is not in the data) and the offer's size. |
| `offer_id`, `offer_name`, `offer_url`, `offer_image_url` | Hwahae's shop item. Its title often carries promo text such as `[only화해] ... 더블 기획`. |
| `catalog_price`, `package_info` | The price and pack size on the catalogue entry. |
| `rating`, `review_count` | Hwahae review score out of 5 and review count. |

#### Example row

Trending, rank 1 on 2026-10-14:

```json
{
  "theme": "trending",
  "theme_name": "급상승",
  "ranking_id": 5102,
  "ranking_name": "카테고리 전체",
  "ranking_url": "https://www.hwahae.co.kr/rankings?english_name=trending&theme_id=5102",
  "ranking_updated_at": "2026-10-14T05:00:00",
  "ranking_size": 50,
  "rank": 1,
  "rank_delta": 0,
  "is_rank_new": false,
  "product_id": 1990260,
  "product_name": "파워10 감초줄렌 젤리패드",
  "product_url": "https://www.hwahae.co.kr/products/1990260",
  "catalog_price": 25000,
  "package_info": "70EA(120ml)",
  "brand_id": 56,
  "brand_name": "잇츠스킨",
  "offer_id": 43022,
  "offer_name": "파워10 감초줄렌 젤리패드(대용량) 130매",
  "offer_url": "https://www.hwahae.co.kr/goods/43022",
  "price": 9500,
  "currency": "KRW",
  "discount_rate": 76,
  "capacity": "130EA/215ml",
  "rating": 4.65,
  "review_count": 7301
}
```

#### Honest nulls

A field Hwahae did not send is `null`, never `0` or `""`.

- **Many products have no shop offer** (223 of 900 rows sampled across 14 rankings on
  2026-10-15; the share varies by ranking and is higher in narrow categories). Hwahae ranks it from its catalogue and reviews but does not sell
  it. On those rows `price`, `discount_rate`, `capacity` and every `offer_*`
  field are `null`; `catalog_price` is still there.
- **`price` and `catalog_price` are different things.** The shop sells bundles,
  refills and double packs, so the shop price can be above or below the
  catalogue price of the single item (higher on 241 of 677 offers sampled).
  Do not compute a discount from the pair; use `discount_rate`.
- `catalog_price` is null for the rare product whose catalogue entry has no
  price (3 of 900 sampled). `capacity` is null when Hwahae sends an empty one.
- `rating` is null for a rating of 0, which would mean "not rated".
- `rank_delta` is whatever Hwahae sent. Every NEW product sampled carried
  a `rank_delta` of 0, so read `is_rank_new` and `rank_delta` together.

### Pricing

Pay per event:

- **$0.002** per run (`actor-start`), charged only after your input is
  validated, so a run that fails on bad input costs nothing.
- **$0.0035** per ranking row written (`ranking-row-scraped`), i.e. $3.50 per
  1,000 rows. The default run (trending, 50 rows) costs about $0.18. A row that
  is not written is not charged.

### How it fetches

The ranking page `hwahae.co.kr/rankings?english_name=<theme>&theme_id=<id>` is
server rendered: it carries the theme's whole category tree and the first 20
rows of the ranking. The tree is what lets you name a ranking instead of
knowing its id. Rows 21 and on are the ones the page itself loads as you
scroll, from Hwahae's own JSON gateway (`gateway.hwahae.co.kr`), 20 per request.
Both answer without a login. A refusal (403, 429, 202, 5xx), a 200 that is not
the page or the JSON, is treated as the address being blocked: the actor rotates
to a fresh one and retries.

Things worth knowing, all measured on 2026-10-15:

- **Only the most specific category is a ranking.** A group such as 스킨케어 has
  no ranking of its own; its child 전체 ("all") is the ranking of the whole
  group. Naming the group picks that child for you. Hwahae's own site answers
  an id that is a group with an error page (HTTP 500), and so does this actor
  (as a failed ranking, no charge).
- **A wrong id is an error, never a refusal.** An id that does not exist is HTTP
  404 and a made-up theme is HTTP 500, both on Hwahae's own error page; the
  actor does not retry them. An id from another theme is served by Hwahae with
  that other theme's rows under this theme's tree; the actor checks the id is
  in the theme's tree and fails the ranking rather than label the rows wrong.
- **Update rhythm differs by theme.** On 2026-10-15 trending had been rebuilt
  on 2026-10-14 at 05:00, and the category, skin and age rankings on
  2026-10-08. Read `ranking_updated_at` before treating two runs as two days
  of movement.
- **Ranking length.** Trending holds 50 rows; the others 100. `maxItems` above
  a ranking's length reads the whole ranking.
- **Search is not covered.** Hwahae's search pages are rendered in the browser
  and are not part of this actor.
- **Brand rankings are not covered.** They rank brands, not products.

# Actor input Schema

## `rankings` (type: `array`):

Which Hwahae rankings to read, one per line. Write a theme (trending, category, skin or age), or a path down that theme's category tree with '>' (for example: trending > 스킨케어, skin > 민감성 > 스킨케어, age > 20대, category > 남성). English names work for the top groups, skin types and age bands (trending > skincare, skin > sensitive, age > 20s). A group means its overall ranking. You can also paste a ranking's page URL from hwahae.co.kr, or write a theme and its numeric id (skin > 687). Defaults to the trending ranking. Brand rankings list brands, not products, and are not supported.

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

Read at most this many positions from each ranking, best rank first. Trending holds 50 rows; category, skin and age rankings hold 100. 20 rows come with each page. A ranking asked for twice is read once.

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

Use Apify's automatic datacenter proxy (the default). A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "rankings": [
    "trending"
  ],
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (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 = {
    "rankings": [
        "trending"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/hwahae-rankings").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 = {
    "rankings": ["trending"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/hwahae-rankings").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 '{
  "rankings": [
    "trending"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/hwahae-rankings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/hwahae-rankings"
        }
    }
}
```

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/wN1yzeZAZYnOOU9kF/builds/czGlTtkaKd4fkqrjZ/openapi.json
