# Nitori Japan Furniture & Home Goods Scraper (`abotapi/nitori-net-jp-scraper`) Actor

Scrape Nitori Japan (nitori-net.jp) furniture and home goods by keyword, category or pasted link. Filter by category, brand, colour and price. Returns name, code, price, original price, images, variants, specifications, delivery and assembly terms and reviews. Incremental mode tracks daily changes.

- **URL**: https://apify.com/abotapi/nitori-net-jp-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Nitori Japan Furniture & Home Goods Scraper

Scrape products from **Nitori Japan** (`nitori-net.jp`), the country's largest
furniture and home-goods retailer. Search the catalogue by keyword, browse a
whole category with the store's own filters, or paste product, category and
search links. Every product comes back as one flat record with price, discount,
images, stock and category; enable details to add the full specifications
(dimensions, material, weight, country of origin), the delivery, assembly and
returns terms, colour and size variants, the original pre-discount price, and
the product's customer reviews.

### What you can do

- **Search by keyword**, for example `ソファ` (sofa), `ベッド` (bed) or
  `カーテン` (curtain), with the store's own filters and sort orders.
- **Browse a whole category** with no keyword at all: pick a Category, Brand or
  Colour and the actor walks the catalogue for you.
- **Paste links or product codes**: product pages, category pages, search
  result pages and bare product codes can be mixed in one list. Product links
  always come back with full detail.
- **Get customer reviews**: average rating, the per-star breakdown, and the
  individual reviews with author nickname, date, rating, title, text, helpful
  count and any customer photos.
- **Track changes over time**: turn on Incremental mode to get only NEW,
  UPDATED and REAPPEARED products on each scheduled run, or resume one specific
  interrupted crawl with `resumeFromRunId`.

### Filters

All of these are the store's OWN filters, so they narrow the result count at
the source rather than after the fact, and they apply in both modes: they
narrow a keyword search, and they also narrow a category or search link you
paste in url mode.

| Filter | What it does |
|---|---|
| **Category** | 37 top-level store categories, from Sofas and Beds to Kitchenware, Lighting and Pet supplies. |
| **Brand** | Nitori, Deco Home, Nitori Business or N+. |
| **Colour** | 25 store colours, from White and Grey through to Navy, Rose and Gold. |
| **Min / Max price (JPY)** | A price band, applied by the store. |
| **Only reduced-price products** | Just the products the store currently flags as reduced. |
| **Minimum average rating** | 3, 4 or 5 stars and up. Applied to each product's own rating. |
| **Only products that can be ordered** | Needs details, because order availability is only published on the product page. |
| **Sort by** | Recommended (the store default), price up or down, newest, highest rated, or most reviewed. |

### Input

| Field | Description |
|---|---|
| **Mode** | `search` or `url`. |
| **Search keywords** | Keywords to search (search mode). Each is searched separately. Leave empty to browse by Category, Brand or Colour alone. |
| **Store links or product codes** | Product links, category links, search links and bare product codes, mixed freely (url mode). |
| **Category / Brand / Colour / price / sale / rating / stock / sort** | See the Filters table above. Applied in both modes. |
| **Fetch product details** | Add description, specifications, delivery and assembly terms, full gallery, videos, manuals, variants and the original pre-discount price. On by default. |
| **Include customer reviews** | Add the aggregate rating, the per-star breakdown and the individual reviews. Requires details. On by default. |
| **Max reviews per product** | Cap on review rows per product. The aggregate rating and count stay complete. Default 20; `0` = all. |
| **Max products** | The run's cap on how many products to return. Default 20; `0` = unlimited. |
| **Max result pages per keyword or link** | Default `0` means no limit, so a run stops only at Max products, at the end of the results, or when a page returns nothing new. |
| **Resume from a previous run** | Continue one specific previous run or dataset: products already collected there are skipped. For recurring monitoring, use Incremental mode instead. |
| **Incremental changes for scheduled runs** | Daily or recurring monitoring of this same search. First run returns everything as `NEW`; later runs return only `NEW`, `UPDATED` and `REAPPEARED`. |
| **State key** | Optional name for a monitoring campaign, so its state stays stable or is deliberately shared. Derived automatically when left empty. |
| **Emit unchanged products** | Incremental mode only. Also return unchanged products, marked `UNCHANGED`. Adds and bills extra rows. |
| **Emit expired products** | Incremental mode only. Also return products no longer found, marked `EXPIRED`, once a run has fully scanned the search. Adds and bills extra rows. |
| **Proxy** | Apify Proxy. The standard pool is the default and is all this store needs. |
| `mcpConnectors` | Optional MCP connectors to export results into (Notion, Linear, Airtable, Apify). |
| `notionParentPageUrl` | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | Cap on items exported to each connector per run. Does not affect the dataset. |

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape; the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Output

One record per product. With details off you get the listing-level fields; with
details on you also get specifications, delivery terms, variants, the original
price and reviews. Example (illustrative values):

```json
{
  "productId": "0000000s",
  "name": "コンパクトカウチソファ (EXAMPLE)",
  "brand": "ニトリ",
  "url": "https://www.nitori-net.jp/ec/product/0000000s/",
  "breadcrumb": ["ソファ", "カウチソファ"],
  "categoryName": "カウチソファ",
  "price": 59990,
  "originalPrice": 69990,
  "discountAmount": 10000,
  "discountPercent": 14.3,
  "discountDate": "2026/08/18",
  "currency": "JPY",
  "onSale": true,
  "isOutlet": false,
  "loyaltyPoints": 272,
  "inStock": true,
  "rating": 4.5,
  "reviewCount": 184,
  "ratingBreakdown": { "1": 1, "2": 3, "3": 9, "4": 47, "5": 124 },
  "reviews": [
    {
      "reviewId": "0000000000000000",
      "title": "イメージ通り",
      "text": "色も素材感も思った通りでした。",
      "rating": 5,
      "date": "2026-05-15T02:04:27Z",
      "author": "レビュー投稿者",
      "authorGender": "女性",
      "authorAgeGroup": "30代",
      "helpfulCount": 4,
      "images": ["https://example.invalid/review-photo.jpg"],
      "variantName": "コンパクトカウチソファ (EXAMPLE BR)"
    }
  ],
  "dimensions": "幅171×奥行126×高さ75cm",
  "packingSize": "梱包1：幅112×奥行77×高さ70cm",
  "weight": "約45.5kg",
  "material": "ポリエステル",
  "color": "グレー",
  "countryOfOrigin": "中国",
  "warrantyYears": "5年",
  "assemblyRequired": true,
  "assemblyTime": "30分",
  "deliveryMethod": "玄関先まで（お客様組立）",
  "assemblyService": "※組立サービス利用可能",
  "shippingNote": "個別送料：1点につき3,300円",
  "storePickup": "可能（送料不要）",
  "returnsPolicy": "14日間返品可能",
  "homeDeliveryAvailable": true,
  "variants": [
    {
      "variantId": "00000000000",
      "skuCode": "0000000",
      "optionType": "カラー",
      "optionValue": "グレー",
      "price": null,
      "onSale": false,
      "selected": true,
      "image": "https://example.invalid/swatch.jpg"
    }
  ],
  "images": ["https://example.invalid/product-01.jpg"],
  "videos": [],
  "assemblyManuals": ["https://example.invalid/manual.pdf"],
  "colorSwatchImages": ["https://example.invalid/swatch.jpg"],
  "badges": ["ネット限定商品"],
  "description": "コンパクトでもしっかり座れる仕様。",
  "specifications": { "size": "幅171×奥行126×高さ75cm", "material": "ポリエステル" },
  "searchMode": "search",
  "scrapedAt": "2026-01-01T00:00:00Z"
}
```

In Incremental mode each record also carries `changeType`
(`NEW` / `UPDATED` / `REAPPEARED` / `UNCHANGED` / `EXPIRED`), `changedFields`,
`firstSeenAt` and `lastSeenAt`.

### Scope: this store serves Japan only

The ticket for this actor asked whether the store serves more than one country,
and whether any sibling Nitori storefront shares the same catalogue. Both were
checked live before this actor was built, and the answer is no on both counts,
so there is deliberately **no `country` input**: it would be a knob that changes
nothing.

- **`nitori-net.jp` is single-market.** It runs one catalogue, in Japanese, priced
  in JPY. Requesting any other language is refused outright by the catalogue
  itself with an unsupported-language error, and no other currency is offered.
- **The sibling Nitori storefronts do not share this catalogue.** Nitori operates
  separate sites for Taiwan, Singapore and South Korea, and each was checked:
  every one runs on a completely different platform with its own product data,
  its own currency and its own URL structure. None of them can be read through
  this actor, and this actor's addresses do not resolve on any of them.

If you need one of those markets, it needs its own actor, not a country input on
this one.

### Reviews

Reviews are real and fully available on this store, and this actor returns them.
Verified live on representative furniture items before the actor was built: a
single popular sofa carried 184 reviews with a complete per-star breakdown, and
34 of those reviews included customer photos.

- `rating` and `reviewCount` are the product's aggregate figures.
- `ratingBreakdown` is the count of reviews at each star level, 1 to 5. It is
  always complete, whatever **Max reviews per product** is set to.
- `reviews[]` holds the individual reviews: author nickname, date, rating,
  title, text, helpful count, photos, and which colour or size variant the
  reviewer bought.
- A product with no reviews returns `reviews: []` and leaves `rating` and
  `reviewCount` as the store reports them, rather than inventing zeros.

Reviews need **Fetch product details** to be on, because they are read from the
product's own page.

### Discounts

The listing pages flag which products are reduced, but only the product page
carries the **original pre-discount price**. So `originalPrice`,
`discountAmount`, `discountPercent` and `discountDate` are populated when
**Fetch product details** is on. With details off you still get `onSale`, but
not the amount. Combine **Only reduced-price products** with details on to pull
a complete discount report.

One consequence worth knowing: `price` is taken from the product's own page,
which is authoritative, and on a reduced product that can be lower than the
price the listing page used to order the results. So with details on, rows
sorted by price are not always in strict price order. The prices themselves are
correct; it is the store's listing index that lags.

### Resume & recurring updates

Two separate features, for two separate jobs.

- **Resume from a previous run** continues ONE interrupted crawl. Paste the run
  or dataset id and this run returns only what that one did not already collect.
  It also survives a platform migration or a Resurrect on its own, using a
  checkpoint stored with the run.
- **Incremental mode** is for scheduled monitoring of the SAME search. The actor
  remembers the previous run itself, keyed on your search and filter setup, and
  classifies every product as `NEW`, `UPDATED`, `REAPPEARED`, `UNCHANGED` or
  `EXPIRED`. Only NEW, UPDATED and REAPPEARED are returned by default, so a
  daily run bills you for what actually changed.

One note on how change detection treats reviews. A product is compared on its
aggregate review figures (`rating`, `reviewCount`, `ratingBreakdown`), not on
the text of individual reviews. A new or deleted review therefore still marks
the product `UPDATED` and still names those fields in `changedFields`, but
`reviews` itself never appears there. This keeps the saved baseline small enough
to track a large catalogue.

### Cost

Charged per product returned, plus one **Detail enrichment** event per product
when **Fetch product details** is on. A run that is capped or has details off
costs less. In Incremental mode a suppressed `UNCHANGED` product is never
charged, even though its product page was still read to detect the change.

### Notes

- Keywords match best in Japanese, since the catalogue is Japanese. Product
  codes and roman-letter model names also work.
- Colour and Brand are union filters: picking two colours returns products in
  either colour, so more selections means more products. Category works the
  other way, because the store treats several categories as a path down its own
  hierarchy, so more selections means fewer products.
- If a run returns nothing and reports rejected requests, that is a connection
  problem rather than an empty search. Enable Apify Proxy and run again.

# Actor input Schema

## `mode` (type: `string`):

Choose 'search' to search the catalogue by keyword and filters, or 'url' to scrape pasted links: product pages, category pages, search result pages and bare product codes are all accepted.

## `queries` (type: `array`):

Only used when mode = search, ignored in url mode. One or more keywords, for example ソファ (sofa), ベッド (bed) or カーテン (curtain). Leave empty to browse by Category, Brand or Colour alone.

## `urls` (type: `array`):

Only used when mode = url, ignored in search mode. Mix freely: product pages under /ec/product/<code>/ , category pages under /ec/cat/... , search pages under /ec/search?q=... , or a bare product code such as 5612330s .

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

Optional. Keep only products in these store categories. Combine with a keyword, or use on its own to browse a whole category. Selecting several returns products matching all of them, so pick one top-level category unless you are deliberately narrowing.

## `brands` (type: `array`):

Optional. Keep only products from these store brands. Selecting several returns products from any of them, so picking more brands returns more products. Leave empty for all brands.

## `colors` (type: `array`):

Optional. Keep only products available in these colours. Selecting several returns products in any of them, so picking more colours returns more products.

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

Optional. Keep only products priced at or above this amount, in Japanese yen. Applied by the store itself, so it narrows the result count at the source.

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

Optional. Keep only products priced at or below this amount, in Japanese yen. Applied by the store itself, so it narrows the result count at the source.

## `onSaleOnly` (type: `boolean`):

Optional. Keep only products the store currently flags as reduced. Turn on Fetch product details as well to get the original pre-discount price and the discount amount, which the listing page does not carry.

## `inStockOnly` (type: `boolean`):

Optional. Keep only products the store will currently accept an order for. Requires Fetch product details, because order availability is only published on the product page.

## `minRating` (type: `string`):

Optional. Keep only products whose average customer rating is at least this many stars. Products with no rating at all are excluded when this is set.

## `sortBy` (type: `string`):

The order the store returns results in. Recommended, most reviewed and newest are the store's own orderings.

## `fetchDetails` (type: `boolean`):

Read each product's own page to add the description, full specifications (dimensions, material, weight, country of origin), delivery, assembly and returns terms, the full image gallery, videos, assembly manuals, the colour and size variants, and the original pre-discount price. Turn off for a faster, cheaper run that returns listing fields only.

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

Add each product's customer reviews: average rating, the per-star breakdown, and the individual reviews (author nickname, date, rating, title, text, helpful count and any photos). Requires Fetch product details. A product with no reviews returns an empty reviews list.

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

Cap on how many individual reviews are returned per product. The average rating, review count and per-star breakdown are always complete regardless of this cap. Use 0 for all reviews a product has.

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

Maximum number of products to return across the whole run. This is the run's cap. Use 0 for unlimited.

## `maxPages` (type: `integer`):

Maximum number of result pages read per keyword, category or link. 0 means no limit, which is the default: the run then stops only at Max products, at the end of the results, or when a page returns nothing new.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large catalogue pull without returning or charging for products already collected there. Use this after an interrupted run. For recurring daily monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns every matching product as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED products. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately for each search, filter and detail setup; use State key to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to let the actor derive a key automatically from the search, filters and detail settings, so different searches never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return products that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return products that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, so never when Max products capped it or when Resume was used. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking.

## `proxy` (type: `object`):

Apify Proxy is recommended. The standard pool is the default and is sufficient for this store.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write or digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "ソファ"
  ],
  "urls": [
    "https://www.nitori-net.jp/ec/product/5612330s/",
    "https://www.nitori-net.jp/ec/cat/Sofa/CouchCornerSofa/1/"
  ],
  "onSaleOnly": false,
  "inStockOnly": false,
  "minRating": "0",
  "sortBy": "relevance",
  "fetchDetails": true,
  "includeReviews": true,
  "maxReviewsPerProduct": 20,
  "maxItems": 20,
  "maxPages": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "queries": [
        "ソファ"
    ],
    "urls": [
        "https://www.nitori-net.jp/ec/product/5612330s/",
        "https://www.nitori-net.jp/ec/cat/Sofa/CouchCornerSofa/1/"
    ],
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/nitori-net-jp-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 = {
    "mode": "search",
    "queries": ["ソファ"],
    "urls": [
        "https://www.nitori-net.jp/ec/product/5612330s/",
        "https://www.nitori-net.jp/ec/cat/Sofa/CouchCornerSofa/1/",
    ],
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/nitori-net-jp-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 '{
  "mode": "search",
  "queries": [
    "ソファ"
  ],
  "urls": [
    "https://www.nitori-net.jp/ec/product/5612330s/",
    "https://www.nitori-net.jp/ec/cat/Sofa/CouchCornerSofa/1/"
  ],
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/nitori-net-jp-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/nitori-net-jp-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/7lmzhmh0Fw27Bo44B/builds/0bJqbCa7o7NaZxfsr/openapi.json
