# @cosme Japan Beauty Rankings — Rating, Review & Price Stats (`jpmarketdata/cosme-beauty-market-checker`) Actor

The @cosme ranking of any Japanese cosmetics category in one call: every ranked product with its 0-7 rating, review count, price points and release date, plus rating and price quartiles, brand concentration, rank movement and awards. Review text is never collected. アットコスメのランキング統計を1コールで。

- **URL**: https://apify.com/jpmarketdata/cosme-beauty-market-checker.md
- **Developed by:** [h ichi](https://apify.com/jpmarketdata) (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 $20.00 / 1,000 category ranking summaries

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## @cosme Japan Beauty Rankings — Rating, Review-Count & Price Stats in One Call

**Which Japanese cosmetics are ranking right now, at what price and at what rating?** One call per category answers it with the **whole top 50** of the [@cosme](https://www.cosme.net/) ranking — the ranking Japanese beauty buyers actually shop off — plus the aggregate distribution behind it.

@cosme is Japan's dominant beauty review platform, and its category rankings are the closest thing the market has to a public demand signal. This Actor turns one ranking into **one compact market record**:

- **`rating` — min / Q1 / median / Q3 / max on @cosme's 0–7 scale**, so you can see whether a category is a tight field of 5.1s or has a real quality gap at the top
- **`priceJpy` — the same quartiles over each product's cheapest listed size**, plus USD at the current rate. What price point is winning this category?
- **`reviewCount` — median and maximum review *counts***. The distinction between a 2002 staple with 8,727 reviews and a 2026 launch with 17 sitting three ranks apart
- **`brandTop` / `brandCount` / `topBrandShare`** — brand concentration: is the category owned by one house or contested by 42?
- **`rankMovement`** — how many of the ranked products moved up, stayed, fell or are new entries. The mix tells you whether the category is churning or frozen
- **`bestCosmeCount`**, **`variantsPerProductMedian`**, and the ranking's own **`rankingUpdatedOn`** and **`aggregationPeriod`**
- Optionally every ranked product (rank, movement, brand, rating, review count, **every size/price pair**, release date, best-cosme flag, product URL)

Built for **brands benchmarking their own position** and **cross-border beauty resellers** deciding what to stock: the ranking is what sells in Japan, and this Actor gives you the price and rating level it sells at.

No login, no API key, nothing stored between runs.

### ⚠️ What this Actor deliberately does **not** collect

@cosme is a review site, so this needs saying plainly:

> **Review text, reviewer names, reviewer profiles and review photos are never collected — by design, permanently.**

| Not collected | Why |
|---|---|
| Review bodies (the text people write) | It is user-authored personal content. Statistics do not need it |
| Reviewer names, ages, skin types, profile pages | Personal data about individuals who are not the subject of this product |
| Review photos | Same — user-uploaded personal content |
| Review permalinks and the product's `/review/` tab URL | Emitting the link is emitting the pointer. **No record ever contains a `.../review/` URL** — `url` is always the product page |

**What *is* collected is the review *count* — a number** — and the aggregate rating value. That is the entire review-side footprint of this Actor.

This is not a promise in prose, it is enforced in code. `src/main.py` carries a `DELIBERATE EXCLUSION` block and an executable guard, `review_text_leaks(record)`, which scans every value of an emitted record for the review markers (`review-body`, `review-text`, `reviewer-desc`, `/reviewer/`, `/review/`, `/reviews/`) and returns the ones it finds. **The test suite asserts it returns `[]` for every emitted record shape.** If a future change ever tag-strips a whole product block or copies the review-tab link, the markers come along with it and the build fails. Every field is read out of the single HTML element that carries its class token — a review body has no path into a record even by accident.

### Pricing — from $0.02 per category, no subscription

| Event | Price | When |
|---|---|---|
| Category ranking summary | **$0.02** | Per category that produced products |
| Individual product | **$0.002** | Only if you enable **Include individual products** |

A default run (1 category, whole top 50, summary only) costs **$0.02**. With individual products on it is $0.02 + 50 × $0.002 = **$0.12**. **A category that returns nothing is never charged.**

### Input

| Field | Example | Notes |
|---|---|---|
| `categories` | `["item/1069"]` | `<axis>/<id>`, axis ∈ `item` / `effect` / `skin` / `age` / `pickup`. A full ranking URL is accepted and normalized. Each category costs $0.02 |
| `pagesPerCategory` | `5` | 10 products per page; **the ranking is only 50 deep**, so 5 is the whole thing and the maximum. Lower it for a fast top-10 read |
| `includeIndividualItems` | `false` | Enable to also get each ranked product as a record (+$0.002 each) |
| `convertToUsd` | `true` | Adds USD price stats at the current exchange rate |

#### Finding a category id

Open any @cosme ranking in a browser and copy the URL: `https://www.cosme.net/categories/item/1069/ranking/` → `item/1069`. The `item` axis is the product-type ranking (cleansers, serums, lipsticks…); `effect`, `skin`, `age` and `pickup` are @cosme's other ranking axes and all work the same way.

Only the **main** ranking is read. If you paste a `ranking-rise` (急上昇), `ranking-age` (年代), `ranking-skin` (肌質) or `ranking-search` (お好み) URL the run fails with a clear message instead of quietly billing you for the plain ranking, which is a different result set with a different aggregation window.

### Output example (`type: "category_summary"`)

Measured on `item/1069` (リキッドクレンジング / liquid cleansers), 2026-08-01:

```json
{
  "type": "category_summary",
  "category": "item/1069",
  "categoryName": "リキッドクレンジング",
  "rankingUpdatedOn": "2026-07-31",
  "aggregationPeriod": { "from": "2026-04-30", "to": "2026-07-29", "raw": "2026/4/30〜2026/7/29" },
  "productsRanked": 50,
  "totalListingsFound": 50,
  "pagesFetched": 5,
  "ratingScale": 7,
  "rating":   { "min": 3.7, "q1": 4.9,  "median": 5.1,  "q3": 5.4,  "max": 6.9,  "count": 50 },
  "priceJpy": { "min": 352, "q1": 1463, "median": 1760, "q3": 3242, "max": 7260, "count": 42 },
  "reviewCount": { "median": 186, "max": 8727, "count": 50 },
  "brandTop": [["ビオデルマ", 4], ["Chacott COSMETICS(チャコット・コスメティクス)", 2], ["ビフェスタ", 2]],
  "brandCount": 42,
  "topBrandShare": 0.08,
  "rankMovement": { "up": 8, "stay": 21, "down": 19, "new": 2, "unknown": 0 },
  "bestCosmeCount": 2,
  "variantsPerProductMedian": 1,
  "checkedAt": "2026-08-01T05:41:12.884Z",
  "sourceUrl": "https://www.cosme.net/categories/item/1069/ranking/",
  "priceUsd": { "min": 2.3, "q1": 9.55, "median": 11.49, "q3": 21.17, "max": 47.41 },
  "exchangeRateJpyUsd": 0.00653
}
```

And one optional product record (`type: "product"`) — note that `url` is the **product** page and every size/price pair is kept:

```json
{
  "type": "product",
  "category": "item/1069",
  "categoryName": "リキッドクレンジング",
  "rank": 1,
  "rankMovement": "stay",
  "rankMovementJa": "順位変わらず",
  "productId": "2892367",
  "name": "サンシビオ エイチツーオー D",
  "brand": "ビオデルマ",
  "brandId": "4680",
  "ratingScale": 7,
  "rating": 5.4,
  "reviewCount": 8727,
  "minPriceJpy": 1463,
  "priceVariants": [
    { "size": "100ml", "priceJpy": 1463 },
    { "size": "250ml", "priceJpy": 3069 },
    { "size": "500ml", "priceJpy": 3810 },
    { "size": "850ml", "priceJpy": 5060 }
  ],
  "priceVariantCount": 6,
  "priceLabelJa": "税込価格",
  "releaseDate": "2002-07-05",
  "releaseDateRaw": "2002/7/5",
  "bestCosme": true,
  "url": "https://www.cosme.net/products/2892367/"
}
```

### Notes on the data

- **The rating scale is 0–7, not 0–5.** @cosme rates on seven points, so a 5.4 is a strong product, not an impossible one. Every record carries `ratingScale: 7` so you cannot compare it against a five-star rating from another site by accident. (To rescale to 5 points: `rating / 7 * 5`.)
- **The aggregation window is part of the answer.** @cosme recomputes each ranking on a rolling ~3-month window and publishes both dates in the page header; both are on every record as `rankingUpdatedOn` and `aggregationPeriod`. Two rankings are only comparable when their windows are — a snapshot without them is not interpretable, which is why they are never omitted.
- **The ranking is 50 deep. That is the whole population, not a sample.** `productsRanked` is normally exactly 50 (10 per page × 5 pages), so the quartiles are the true quartiles of the ranked set rather than an estimate. There is no page 6.
- **One product, many prices.** A Japanese cosmetic is usually sold in several sizes on one line (「税込価格：100ml・1,463円 / 250ml・3,069円 / 500ml・3,810円」). All pairs are parsed; `minPriceJpy` — the **cheapest listed size** — is the representative price used in `priceJpy`, because a product that also sells an 850 ml bottle is not "more expensive" than one that only sells 100 ml. `priceVariantCount` tells you how many sizes there were.
- **Not every product has a price.** Open-price products (「オープン価格」) and refill-only lines carry a size with no yen amount; they keep `priceJpy: null` and are excluded from `priceJpy`, which is why `priceJpy.count` (42 above) can be lower than `productsRanked` (50). Nothing is invented to fill the gap.
- **Prices are the tax-included list prices @cosme publishes** (`priceLabelJa` records the site's own label), not shop prices and not sold prices.
- **Rank movement** is @cosme's own icon: `up` (including its 「10位以上順位アップ」 variant), `stay`, `down`, `new` (ランキング初登場). An unrecognized icon is reported as `unknown` rather than folded into `stay`.
- **`bestCosme`** flags products carrying the ベストコスメ award badge on the ranking page.
- **Brand names are read from the brand link only.** Brands with a paid tie-up carry a second link whose text is a notice; it is excluded, so one brand stays one entry in `brandTop`.
- **Encoding**: @cosme serves Shift\_JIS, which is decoded explicitly. Japanese brand, product and category names come through intact.

### What this Actor does not do

- **No review text, ever.** See the section above — this is the product's hard constraint, not a limitation
- **No product dumps by default.** The product is the statistic; individual products are opt-in and separately priced
- **No login-only data and no `/api/` paths.** Everything comes from public ranking pages; the paths @cosme disallows in robots.txt are never requested
- **No stored dataset.** Every run fetches live; nothing is retained between runs
- **No browser.** Plain HTTP, 256 MB, a run stays well inside 120 s

### Reliability

Requests are throttled to 1.5 s and a soft 95 s wall-clock budget keeps a multi-category run inside its timeout. Page 1 of every category always runs, so every category produces a summary; if the budget stops the follow-up pages, the affected summary says so with `truncatedForTimeLimit: true` and a smaller `pagesFetched` — a top-10 read is never passed off as the full top 50. If **every** category fails the run fails loudly rather than returning an empty success.

# Actor input Schema

## `categories` (type: `array`):

One or more @cosme ranking categories, written as '<axis>/<id>' — for example 'item/1069' (リキッドクレンジング). The axis is one of item, effect, skin, age, pickup. You can also paste a full ranking URL (https://www.cosme.net/categories/item/1069/ranking/) and it will be normalized for you. Only the main ranking is read: the ranking-rise / ranking-age / ranking-skin / ranking-search variants are different result sets and are rejected rather than silently swapped. Each category costs $0.02.

## `pagesPerCategory` (type: `integer`):

How deep to read the ranking. @cosme publishes 10 products per page and the ranking stops at page 5, so 5 (the default) is the whole top 50 and there is nothing beyond it. Lower it to 1 for a fast top-10 read.

## `includeIndividualItems` (type: `boolean`):

Off by default: a run costs a flat $0.02 per category summary. Enable to also get every ranked product as its own record (rank, rank movement, brand, 0-7 rating, review count, every size/price pair, release date, best-cosme flag, product URL) at +$0.002 per product. Review text, reviewer names and review photos are never included — see the README.

## `convertToUsd` (type: `boolean`):

Adds USD price statistics next to JPY using the current exchange rate (open.er-api.com).

## Actor input object example

```json
{
  "categories": [
    "item/1069"
  ],
  "pagesPerCategory": 5,
  "includeIndividualItems": false,
  "convertToUsd": true
}
```

# 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 = {
    "categories": [
        "item/1069"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jpmarketdata/cosme-beauty-market-checker").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 = { "categories": ["item/1069"] }

# Run the Actor and wait for it to finish
run = client.actor("jpmarketdata/cosme-beauty-market-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "categories": [
    "item/1069"
  ]
}' |
apify call jpmarketdata/cosme-beauty-market-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=jpmarketdata/cosme-beauty-market-checker",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/L6ZluiOBAMGNgiBdr/builds/QXMqfjHYBDhRo97fL/openapi.json
