# Rakuten Japan Products, Prices & Reviews Scraper (`huggable_quote/rakuten-japan-scraper`) Actor

Search Rakuten Japan (Rakuten Ichiba) and collect products with prices, SKU options, stock, shop and rating data - plus customer reviews when you want them. No browser and no API key needed. Detail and review collection are optional, and you pay per result (pay-per-event).

- **URL**: https://apify.com/huggable\_quote/rakuten-japan-scraper.md
- **Developed by:** [OrbitData Labs](https://apify.com/huggable_quote) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 search products

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?

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

## Rakuten Japan Products, Prices & Reviews Scraper

Search Rakuten Ichiba (楽天市場) by keyword, search URL or genre — or hand it
product URLs directly — and get back clean, flat rows: product name, price,
reference price, stock, SKU options, shop, rating, shipping, Rakuten points and
search rank, plus customer reviews when you ask for them. It runs over plain
HTTP with no browser and no API key, and you pay per result.

### What you can collect

| Input | What you get |
| --- | --- |
| `searchQueries` | Keyword search results, de-duplicated across keywords |
| `searchUrls` | Any Rakuten search or category URL, filters preserved |
| `genreIds` | A whole genre (category) listing |
| `productUrls` | Specific product pages, no search needed |

All four can be combined in one run. Products are de-duplicated globally on
`shopId + itemId`, and every place a product was found is kept in `foundIn`.

### Quick start

Search only — the cheapest mode:

```json
{
  "searchQueries": ["イヤホン"],
  "maxItems": 100
}
```

Search, then enrich each product with its detail page:

```json
{
  "searchQueries": ["ワイヤレスイヤホン"],
  "maxItems": 50,
  "includeDetails": true
}
```

Search → detail → reviews:

```json
{
  "searchQueries": ["イヤホン"],
  "maxItems": 20,
  "includeDetails": true,
  "includeReviews": true,
  "maxReviewsPerProduct": 30
}
```

A search URL you already have, cheapest under ¥5,000, newest first:

```json
{
  "searchUrls": ["https://search.rakuten.co.jp/search/mall/イヤホン/"],
  "maxPrice": 5000,
  "sort": "newest",
  "maxItems": 100
}
```

Specific products, no search:

```json
{
  "productUrls": ["https://item.rakuten.co.jp/jblstore/wave-beam2/"],
  "includeReviews": true,
  "maxReviewsPerProduct": 50
}
```

### Output

One dataset holds four record types, told apart by `type`. The Console shows
four ready-made views: **Products**, **Reviews**, **Failures** and
**Run statistics**. Views pick which *columns* to show, so filter on `type` when
you export if you want one type per file.

#### `product`

```json
{
  "type": "product",
  "detailIncluded": true,
  "itemId": 10000198,
  "shopId": 398768,
  "manageNumber": "wave-beam2",
  "shopUrlCode": "jblstore",
  "shopName": "JBL・AKG公式ストア",
  "url": "https://item.rakuten.co.jp/jblstore/wave-beam2/",
  "shopUrl": "https://www.rakuten.co.jp/jblstore/",
  "name": "＼楽天1位／【公式限定】JBL ワイヤレスイヤホン WAVE BEAM 2 …",
  "currency": "JPY",
  "price": 8030,
  "referencePrice": 8030,
  "referencePriceLabel": "当店通常価格",
  "taxIncluded": true,
  "taxRate": 0.1,
  "isSoldOut": false,
  "isPurchasable": true,
  "stockQuantity": 332,
  "freeShipping": true,
  "shippingPrice": 0,
  "points": 1460,
  "pointRate": 20,
  "pointCampaignStart": "2026-08-24T11:00:00.000Z",
  "pointCampaignEnd": "2026-08-27T00:59:59.000Z",
  "reviewCount": 526,
  "reviewAverage": 4.39,
  "brand": "JBL",
  "genreId": 502835,
  "categoryPath": ["TV・オーディオ・カメラ", "オーディオ", "ヘッドホン・イヤホン"],
  "variantSelectors": [{ "label": "カラー", "values": ["ブラック", "ホワイト", "ブルー", "ピンク"] }],
  "skus": [
    {
      "variantId": "JBLWBEAM2PIK",
      "selectorValues": ["ピンク"],
      "price": 8030,
      "points": 1606,
      "stockQuantity": 71,
      "articleNumber": "4968929222629",
      "freeShipping": true
    }
  ],
  "images": ["https://thumbnail.image.rakuten.co.jp/…"],
  "hasVariants": true,
  "isAd": true,
  "searchQuery": "イヤホン",
  "searchRank": 12,
  "searchPage": 1,
  "searchPosition": 17,
  "sourceSearchUrl": "https://search.rakuten.co.jp/search/mall/…",
  "foundIn": [
    { "searchQuery": "イヤホン", "searchRank": 12, "searchPage": 1, "searchPosition": 17,
      "sourceSearchUrl": "https://search.rakuten.co.jp/search/mall/…", "isAd": true }
  ],
  "scrapedAt": "2026-08-31T08:18:11Z"
}
```

Without `includeDetails`, the same record arrives with `detailIncluded: false`
and without the detail-only fields (`skus`, `stockQuantity`, `taxRate`,
`referencePriceLabel`, `pointCampaign*`, `brand`, `variantSelectors`).

#### `review`

```json
{
  "type": "review",
  "reviewId": "7nrx-i9awf-fxgqwu_1",
  "itemId": 10000198,
  "shopId": 398768,
  "rating": 5,
  "title": null,
  "body": "…",
  "postDate": "2025/11/30",
  "orderDate": "2025/11/25",
  "purchasedOption": "カラー:ホワイト",
  "helpfulCount": 0,
  "reviewUrl": "https://review.rakuten.co.jp/item/1/398768_10000198/1.1/",
  "page": 1,
  "scrapedAt": "2026-08-31T08:19:24Z"
}
```

`title` is `null` on the many Rakuten reviews that have no title — it is never
invented from the review body.

#### `failure` and `run-stats`

```json
{
  "type": "failure",
  "url": "https://example.com/not-a-rakuten-url/",
  "stage": "input",
  "reason": "invalid_product_url",
  "detail": "unsupported product url",
  "scrapedAt": "2026-08-31T07:42:54Z"
}
```

`stage` is `input`, `search`, `product` or `review`. One `run-stats` row closes
every run with request counts, stored counts, partial successes and the amount
charged; the same numbers go to the `RUN_STATS` key-value record.

### Pricing

You pay per stored result. **There is no charge for starting a run.**

| Event | Price | Charged when |
| --- | ---: | --- |
| `search-product` | $0.0015 | A product found by search is stored |
| `product-detail` | $0.0035 | A product's detail page is collected and stored |
| `review` | $0.0015 | A review is stored |

- Search only → `search-product` per product.
- Search + detail → both events for the same product, **$0.0050** together.
- Product URLs → `product-detail` only ($0.0035), no search charge.
- Reviews → one `review` per stored review, on top of the product.

| Example run | Charge |
| --- | ---: |
| 100 products, search only | $0.15 |
| 100 products, with detail | $0.50 |
| 100 products, detail + 10 reviews each | $2.00 |
| 1,000 products, search only | $1.50 |
| 1,000 products, with detail | $5.00 |
| 1,000 product URLs, detail only | $3.50 |
| 10,000 reviews | $15.00 |

**You are never charged for** failure records, a product whose detail request
failed (you pay `search-product` only), duplicates found under several keywords,
duplicate reviews, empty review pages, searches that return nothing, retries, the
`run-stats` row, or sponsored rows you excluded.

Every run logs its worst case before doing any work, and what it actually
charged at the end:

```
worst case charge: search-product        2 x $0.0015 = $0.0030
worst case charge: product-detail        2 x $0.0035 = $0.0070
worst case charge: review                4 x $0.0015 = $0.0060
worst case total: $0.0160
...
charged total: $0.0160
```

To cap a run, set Apify's **maximum total charge** on it. The Actor honours it,
stops storing chargeable results at the limit and reports `budgetExhausted`.

### Input reference

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `searchQueries` | array | — | Keywords, e.g. `イヤホン`. |
| `searchUrls` | array | — | Rakuten search/category URLs. Filters kept; a page number in the URL is ignored. |
| `genreIds` | array | — | Genre IDs, e.g. `502835`. |
| `productUrls` | array | — | Product pages to collect directly. |
| `maxItems` | integer | `100` | Stop after this many **unique** products. Main cost control. |
| `maxPagesPerQuery` | integer | `5` | Depth per starting point; capped at 150. |
| `includeDetails` | boolean | `false` | Fetch each product's detail page (+$0.0035 each). |
| `includeReviews` | boolean | `false` | Collect reviews (+$0.0015 each). Requires `includeDetails`. |
| `maxReviewsPerProduct` | integer | `100` | Per product. Rakuten returns 30 reviews per request. |
| `sort` | string | `standard` | `price_asc`, `price_desc`, `newest`, `review_count`, `review_average`, `total_price_asc`, `total_price_desc`. |
| `minPrice` / `maxPrice` | integer | — | Price bounds in JPY. |
| `includeSoldOut` | boolean | `false` | Rakuten hides sold-out products from search by default. |
| `includeAds` | boolean | `true` | Keep sponsored rows. `false` collects organic results only. |
| `requestDelaySecs` | integer | `2` | Below `1.5` is raised to `1.5`. |
| `maxConcurrency` | integer | `1` | Reserved; this version always runs sequentially. |
| `proxyConfiguration` | object | disabled | Optional; the Actor works from plain datacenter IPs. |

Unusable URLs and keywords never stop a run — each becomes a `failure` record.

### Sponsored products

Rakuten shows up to five sponsored rows per search page. They are **kept by
default** and flagged `isAd: true`, because that is what a shopper sees. A
product that is also an organic result keeps its organic `searchRank`. Set
`includeAds: false` to drop rows that appear *only* as ads — they then use
neither your `maxItems` budget nor any charge.

### Failures and partial success

Problems are isolated per product, so a run keeps everything it did collect:

- a product whose **detail** request failed is still stored with its search data,
  `detailIncluded` stays `false`, and its reviews are **not** requested;
- a product whose **reviews** failed is still stored with its detail data;
- a failing search page ends that keyword's paging only; other keywords continue.

Each is counted in `partialSuccesses`.

### Privacy

- Only **public** product and review pages are read. The Actor never logs in and
  never touches private or account data.
- Reviewer identity is **not** collected: no nickname, age band, sex, reviewer
  ranking or reviewer-uploaded photos. Rating, review text, dates, purchased
  option and helpful count are collected.
- Values missing from the page are reported as `null` — never guessed.
- You are responsible for using the collected data in line with applicable law
  and Rakuten's terms. This is not legal advice.
- This Actor is an independent tool and is **not affiliated with, endorsed by, or
  operated by Rakuten**. "Rakuten" and "楽天市場" are used only to describe the
  public data source.

### Request behaviour

Defaults follow what was measured against Rakuten (200 sequential requests from
one datacenter IP produced no blocks):

| Setting | Value |
| --- | --- |
| Concurrency | 1 |
| Delay between requests | 1.6s (floor 1.5s) |
| Retries | 2, transient failures only |
| Backoff | 5s, then 15s |
| Browser | not used |
| Proxy | off by default |

An HTTP 200 carrying an Akamai stub is treated as a block, not a success, and is
retried. 404s, unexpected page formats and empty-but-valid pages are never
retried. If the embedded page data changes shape, the run records a structured
failure and logs it — it never reports a silent zero-result.

### Not supported in this version

- The Rakuten Web Service API mode (bring-your-own application ID)
- Whole-shop crawling and ranking pages
- Comparing every seller's price for one product
- Price history
- Anything requiring a login, a browser or CAPTCHA solving

### Known limitations

- Sold-out detection on **detail** pages is not verified against a live sold-out
  item; that fixture is derived. The search field `isSoldOut` comes straight from
  Rakuten and is reliable.
- Legacy non-SKU detail pages were not found while sampling; that path is covered
  by a derived fixture only.
- `maxConcurrency` above 1 is not implemented — only concurrency 1 was validated.
- Sustained volume beyond 200 requests has not been measured.
- Rakuten serves at most 6,750 results (150 pages) per search; deeper paging is
  not possible, and the Actor stops rather than looping.

### Reporting a problem

Please include: the run ID, the input you used, the `failure` rows from the
dataset, and the `RUN_STATS` record. Never paste cookies, tokens or account
credentials.

### Development

```bash
python -m pytest tests -q        # fixture-based tests, no network
python -m mypy src               # strict
python tools/scan_sensitive.py   # secret and personal-data scan
apify run                        # local run - no PPE pricing, so nothing is charged
```

To exercise the billing code locally without creating a real charge, run with
Apify's test flag and the committed prices:

```bash
ACTOR_TEST_PAY_PER_EVENT=true \
  APIFY_ACTOR_PRICING_INFO="$(python -c 'import json;d=json.load(open(".actor/pricing.json"));d.pop("_comment",None);print(json.dumps(d))')" \
  APIFY_CHARGED_ACTOR_EVENT_COUNTS='{"search-product":0,"product-detail":0,"review":0}' \
  apify run
```

Charges then go to `storage/datasets/charging-log/` instead of being billed.
`.actor/pricing.json` is the configuration to enter in the Apify Console;
`tests/unit/test_pricing_config.py` keeps it in step with the code.

Fixtures under `tests/fixtures/` are captured from real public responses and
sanitised — see `tests/fixtures/PROVENANCE.md`. Tests, fixtures, tooling and the
phase-1 probe stay in the repository but are excluded from the Actor upload by
`.actorignore`.

# Actor input Schema

## `searchQueries` (type: `array`):

Japanese or English keywords to search, e.g. イヤホン. Each keyword is crawled separately and results are de-duplicated across all of them. Cost: about 1 request per result page (45 products per page).

## `searchUrls` (type: `array`):

Rakuten search URLs you already have, e.g. https://search.rakuten.co.jp/search/mall/イヤホン/?s=2 . Filters you set in the URL are kept; any page number in it is ignored.

## `genreIds` (type: `array`):

Rakuten genre IDs, e.g. 502835 for headphones. Each becomes its own category crawl.

## `productUrls` (type: `array`):

Skip search and collect these Rakuten Ichiba product pages directly, e.g. https://item.rakuten.co.jp/<shop>/<item>/ . These are charged as product detail ($0.0035 each) only - no search charge. Can be combined with the search inputs above.

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

Stop after this many unique products across all searches. This is the main cost control: you are charged $0.0015 per stored search product, plus $0.0035 each if 'Include product detail' is on.

## `maxPagesPerQuery` (type: `integer`):

How deep to page each keyword, URL or genre. One page returns up to 45 products. Rakuten only serves 150 pages per search, so higher values are capped.

## `includeDetails` (type: `boolean`):

Off by default (cheapest). When off, products carry only what the search results show and cost $0.0015 each. When on, each product costs $0.0035 more (so $0.0050 in total) and gains SKUs, stock, tax, point campaign, brand and description data.

## `includeReviews` (type: `boolean`):

Collect public reviews as separate records at $0.0015 each. Requires 'Include product detail'. Reviews are the largest cost driver: 100 products x 100 reviews is $15.

## `maxReviewsPerProduct` (type: `integer`):

Upper bound on reviews per product, at $0.0015 per stored review. Rakuten returns 30 reviews per request.

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

Result order, matching the sort menu on Rakuten's own search page.

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

Only return products at or above this price.

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

Only return products at or below this price.

## `includeSoldOut` (type: `boolean`):

Rakuten hides sold-out products from search by default. Turn this on to include them.

## `includeAds` (type: `boolean`):

Rakuten shows up to five sponsored rows per search page. Keep them (default) to see the page as a shopper does, or switch this off to collect organic results only. A sponsored product that also ranks organically is always kept, with its organic rank. Excluded ads do not use up your 'Max products' budget and are never charged for.

## `requestDelaySecs` (type: `integer`):

Politeness delay. Values below 1.5s are raised to 1.5s, the pace validated against Rakuten without triggering blocks. Higher values make a run slower but gentler.

## `maxConcurrency` (type: `integer`):

Reserved for a future release. This version always runs sequentially (1).

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

Optional. Disabled by default; the actor works from plain datacenter IPs, so leaving this off costs nothing.

## Actor input object example

```json
{
  "searchQueries": [
    "イヤホン"
  ],
  "maxItems": 100,
  "maxPagesPerQuery": 5,
  "includeDetails": false,
  "includeReviews": false,
  "maxReviewsPerProduct": 100,
  "sort": "standard",
  "includeSoldOut": false,
  "includeAds": true,
  "requestDelaySecs": 2,
  "maxConcurrency": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Every product, review, failure and run statistics record stored in the default dataset. Filter on the `type` field to separate them.

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

Product rows only: name, price, reference price, stock, shop, rating, shipping, Rakuten points and search rank.

## `reviews` (type: `string`):

Customer review rows only: rating, title, text, post and purchase dates, purchased option and helpful count.

## `failures` (type: `string`):

Inputs that could not be collected, with the stage and reason for each.

## `runStatistics` (type: `string`):

One summary row per run: requests made, records stored, partial successes and the amount charged.

# 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 = {
    "searchQueries": [
        "イヤホン"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("huggable_quote/rakuten-japan-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 = { "searchQueries": ["イヤホン"] }

# Run the Actor and wait for it to finish
run = client.actor("huggable_quote/rakuten-japan-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 '{
  "searchQueries": [
    "イヤホン"
  ]
}' |
apify call huggable_quote/rakuten-japan-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,huggable_quote/rakuten-japan-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/UMDr4FNa7FZzGHvhY/builds/g4n53jElYeSasmeOA/openapi.json
