# Japan Matcha & Tea Stock & Price Monitor (`datalab-jp/japan-matcha-tea-stock-monitor`) Actor

Monitor official Japanese tea-shop online stores (d:matcha Kyoto, Horii Shichimeien, Yamamotoyama): matcha and Japanese tea stock, sell-outs, price changes, purchase-limit notices and new products per size.

- **URL**: https://apify.com/datalab-jp/japan-matcha-tea-stock-monitor.md
- **Developed by:** [Task Data](https://apify.com/datalab-jp) (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 $2.00 / 1,000 product variant rows

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?

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

## Japan Matcha & Tea Stock & Price Monitor

Track **stock, prices, purchase-limit notices and new products** at official Japanese tea-shop online stores — **d:matcha Kyoto**, **Horii Shichimeien** (Uji) and **Yamamotoyama** (Tokyo) — in one run. You get one row per **size / tin (variant)** with **price, in-stock flag, SKU, purchase-limit notice and URL**, and in monitoring mode only **what changed since the last run**: back in stock, sold out, price up, price down, new product. No coding needed.

Built for tea retailers, cafés, importers and wholesale buyers who want to follow availability and prices of Japanese matcha and tea without checking each website by hand.

### What data can you extract?

| Field | Description |
|---|---|
| `storeName`, `storeId`, `storeDomain`, `market` | Which official store the row comes from |
| `title`, `variantTitle` | Product title and size/tin (e.g. `抹茶（自園） 無門` / `20g`) |
| `teaCategory` | matcha, gyokuro, kabusecha, sencha, hojicha, genmaicha, bancha, black_tea, other_tea, teabag, gift_set, sweets, teaware, event, other |
| `price`, `compareAtPrice`, `currency` | Current price (JPY) and the store's compare-at price if set |
| `available` | The store's in-stock flag for that exact size |
| `sku`, `productId`, `variantId`, `handle` | Identifiers (SKU only where the store sets one) |
| `purchaseLimitQty`, `purchaseLimitText` | Per-customer limit found in the product description (e.g. `お一人様3点まで` → 3), a short excerpt (max 120 characters) of the notice, or the store's purchase-limit tag (e.g. `tag: 抹茶購入制限商品`) |
| `changeType`, `changes` | `restocked`, `sold_out`, `price_up`, `price_down`, `new_product`, `new_variant`, `purchase_limit_changed`, `removed`, `first_run`, `unchanged` |
| `previousPrice`, `previousAvailable`, `priceChange`, `priceChangePct`, `previousRunAt` | Values at the previous run and the difference |
| `productUrl`, `variantUrl`, `imageUrl` | Product page, page with the size preselected, and the image URL on the store's CDN (reference only) |
| `shippingWeightGrams` | Shipping weight set by the store (package weight, not tea content) |
| `publishedAt`, `productCreatedAt`, `variantUpdatedAt`, `firstSeenAt`, `scrapedAt` | Timestamps |

Product descriptions are **not** copied — only a short purchase-limit excerpt. Images are referenced by URL, not downloaded.

### Why monitor Japanese tea shops?

1. **Stock monitoring for matcha** — popular matcha grades go out of stock and come back. Schedule the Actor with `changeTypes: ["restocked"]` and get only the sizes that are available again.
2. **Price tracking** — several Japanese tea shops revised prices during the 2025 matcha shortage. `price_up` / `price_down` rows give you the date, old price, new price and percentage.
3. **Retail and wholesale purchasing decisions** — see per-size availability and purchase-limit notices across several shops at once to plan what to stock.
4. **Price research and market reports** — a full snapshot of every product, size and price, ready for a spreadsheet or BI tool.
5. **AI agents / RAG** — the Actor is callable through the Apify MCP server, so an AI assistant can answer "which matcha sizes are in stock at d:matcha right now?" with live data.

### How to use it (no coding)

1. Click **Try for free**.
2. Pick the **tea shops** and **categories** (default: all shops, matcha only).
3. Choose **Mode**: `changes` (monitoring) or `snapshot` (everything now).
4. Click **Start**. The first `changes` run stores a baseline and returns everything as `first_run`.
5. Open **Schedules** and run it every few hours or daily. From the second run on you get only the changes — connect a webhook, Slack, email or Google Sheets integration to be notified.

### Input example

```json
{
  "mode": "changes",
  "stores": ["dmatcha", "horii-shichimeien", "yamamotoyama"],
  "categories": ["matcha"],
  "keywords": [],
  "changeTypes": ["restocked", "price_up", "price_down", "new_product"],
  "maxItems": 1000,
  "stateStoreName": "japan-matcha-tea-monitor",
  "proxyConfiguration": { "useApifyProxy": true }
}
```

- `mode` — `changes` returns only differences from the previous run; `snapshot` returns every matching variant. When omitted (API calls), the default is `snapshot`.
- `categories` — defaults to `["matcha"]`; pass `[]` for every product (Yamamotoyama also sells nori, which falls under `other`).
- `keywords` — optional product-name filter (e.g. `奥の山`). Store titles are Japanese; English/Chinese tea words such as `matcha` → `抹茶` or `焙茶` → `ほうじ茶` are retried automatically when they match nothing.
- `stateStoreName` — a named key-value store **in your own Apify account** that remembers the previous prices and stock. Use a different name for each schedule with different filters.

### Output example

```json
{
  "storeId": "horii-shichimeien",
  "storeName": "堀井七茗園",
  "storeDomain": "horiishichimeien.com",
  "market": "japan",
  "productId": "7061885681801",
  "variantId": "40895584731273",
  "handle": "matcha-mumon",
  "title": "抹茶（自園） 無門",
  "variantTitle": "20g",
  "sku": null,
  "productType": null,
  "vendor": "堀井七茗園",
  "tags": ["抹茶再販", "抹茶購入制限商品", "自園購入制限商品"],
  "teaCategory": "matcha",
  "price": 4860,
  "compareAtPrice": null,
  "currency": "JPY",
  "available": false,
  "shippingWeightGrams": 100,
  "purchaseLimitQty": null,
  "purchaseLimitText": "tag: 抹茶購入制限商品",
  "imageUrl": "https://cdn.shopify.com/s/files/1/0566/5308/6857/products/MG_8633.jpg?v=1666257268",
  "productUrl": "https://horiishichimeien.com/products/matcha-mumon",
  "variantUrl": "https://horiishichimeien.com/products/matcha-mumon?variant=40895584731273",
  "publishedAt": "2025-03-13T13:22:57+09:00",
  "productCreatedAt": "2022-10-20T14:03:28+09:00",
  "variantUpdatedAt": "2026-10-10T18:01:14+09:00",
  "scrapedAt": "2026-10-10T11:53:55.844Z",
  "changeType": "sold_out",
  "changes": ["sold_out"],
  "previousPrice": 4860,
  "previousAvailable": true,
  "priceChange": 0,
  "priceChangePct": 0,
  "previousRunAt": "2026-10-10T08:53:55.844Z",
  "firstSeenAt": "2026-10-01T08:00:00.000Z"
}
```

(The product data above is real; the change values illustrate how a `sold_out` row looks.)

**Field notes**

- `changeType` is the most important change; `changes` lists all of them (a size can be `restocked` and `price_up` in the same run).
- `removed` rows describe a size that disappeared from the store since the last run; they carry the last known title and price (`previousPrice`) with `price` and `available` set to `null`. Removed rows are only reported when the whole catalogue was read successfully.
- `purchaseLimitText` is filled when the description mentions a limit or the store tags the product as purchase-limited; `purchaseLimitQty` only when a number is stated. Limits applied only at checkout are not visible.
- Prices are the price shown in each store's product feed in JPY.
- The `RUN_REPORT` record (default key-value store) has a per-store summary: HTTP status, variants read, rows returned, change counts, whether the baseline was updated, and a plain-language hint when a store returned nothing.

### How much does it cost?

Pay-per-event: a tiny start charge of $0.00005 (Apify's default Actor Start event, charged once per GB of memory; this Actor runs on 1 GB) plus a per-row price:

| Event | What it is | Price |
|---|---|---|
| Actor Start (`apify-actor-start`) | Once per run (per GB of memory) | $0.00005 |
| Result row (`apify-default-dataset-item`) | Each row returned | $0.003 (FREE and BRONZE plans) · $0.002 (SILVER and above) |

In `changes` mode you pay only for rows that changed, so a monitor that finds 5 changes costs about $0.015 on the FREE plan. A matcha snapshot of all three stores is about 55 rows (≈ $0.17 on FREE); every product in every store is about 510 rows (≈ $1.53). Use `maxItems` to cap spend. Platform usage (compute) is included in these prices; a run takes about 10 seconds.

### Integrations: API, MCP, and AI agents

This Actor is **read-only**: it only reads public product feeds. It never logs in, adds to cart, orders or changes anything on the stores.

#### API (curl)

```bash
curl -X POST "https://api.apify.com/v2/acts/datalab-jp~japan-matcha-tea-stock-monitor/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" -d '{"mode": "snapshot", "categories": ["matcha"], "maxItems": 1000}'
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("datalab-jp/japan-matcha-tea-stock-monitor").call(run_input={"mode": "snapshot", "categories": ["matcha"], "maxItems": 1000})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('datalab-jp/japan-matcha-tea-stock-monitor').call({ mode: 'snapshot', categories: ['matcha'], maxItems: 1000 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

The synchronous `run-sync-get-dataset-items` endpoint waits up to 300 seconds; a full run normally finishes in well under a minute.

#### Use with AI agents (MCP)

Add the Apify MCP server (`https://mcp.apify.com`) to Claude, ChatGPT, Cursor or any MCP client and ask, for example, "Which matcha sizes are in stock at d:matcha and Horii Shichimeien right now?" The agent calls this Actor with `mode: "snapshot"` and the matching `stores` and `categories`.

### Related Actors

Other Japan data Actors by the same developer:

- [**Japan Knife Restock & Price Monitor**](https://apify.com/datalab-jp/japan-handmade-knife-restock-monitor) — stock and price changes at Japanese knife stores
- [**Japan Denim Restock & Price Monitor**](https://apify.com/datalab-jp/japan-denim-restock-monitor) — stock and price changes at Japanese denim stores
- [**Mercari Japan Scraper – Listings & Item Details**](https://apify.com/datalab-jp/mercari-japan-listings-scraper) — Mercari listings and sold prices
- [**Yahoo Auctions Japan Scraper – Live, Sold & Flea Market**](https://apify.com/datalab-jp/yahoo-auctions-flea-market-scraper) — Yahoo! Auctions and Yahoo! Flea Market listings

### FAQ

**Is this legal?**
The Actor reads only the public product feed (`/products.json`) that each store serves to every visitor, without logging in or bypassing any access control, one request at a time with pauses. It returns factual data (names, sizes, prices, stock) and does not copy product descriptions or images. Each store's robots.txt and terms were read in full before the store was added; stores whose terms restrict automated access or use of their information are not supported. You are responsible for using the data in line with applicable laws and each store's terms.

**Can I use it through the API?**
Yes — see the curl, Python and JavaScript examples above. Schedule it in Apify and read the dataset, or connect a webhook.

**Can AI agents use it?**
Yes. It is read-only, needs no credentials for the stores, and every input has a default, so an agent can call it safely.

**How does change detection work?**
After each run the Actor saves the price and stock of every size in the named key-value store `stateStoreName` in your account. The next run compares against it. The first run per store (or a new store name) returns everything as `first_run`.

**Why is my favourite tea shop not supported?**
A store is added only if it serves a public product feed and its terms of use, read in full, do not restrict this kind of use. Several well-known shops were checked and left out for that reason.

**Does it buy or reserve items?**
No. It does not add to cart, check out or reserve anything, and it does not help get around purchase limits. It only reports what the stores publish.

**Do I need a proxy?**
The default uses Apify's datacenter proxy because Shopify-based stores rate-limit busy cloud IP addresses. Residential proxy is not needed. If a store answers HTTP 429 repeatedly, the run report says so; run again later.

### Disclaimer

This Actor is not affiliated with, endorsed by or connected to any of the tea shops listed. Store names are used only to identify the data source. Data is provided as published by the stores at the time of the run and may change at any time.

### 中文简介

- 监控 d:matcha Kyoto、堀井七茗园、山本山等日本茶铺官方网店的抹茶与日本茶库存、价格、限购说明和新品。
- 每个规格（克数/罐）一行：价格、是否有货、SKU、商品链接；监控模式只返回与上次相比的变化（补货、售罄、涨价、降价、新品）。
- 适用于零售与批发的采购判断、价格调研和库存监控；只读取公开商品数据，不会下单或加入购物车。

# Actor input Schema

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

What to return: 'changes' = only what changed since the previous run (restocked, sold_out, price_up, price_down, new_product, purchase_limit_changed, removed); 'snapshot' = every matching variant with current price and stock. In 'changes' mode the first run per store returns everything as 'first_run' (baseline); schedule later runs to get alerts. Defaults to 'snapshot' when omitted. Both modes compare with and update the state in `stateStoreName`.

## `stores` (type: `array`):

Official Japanese tea-shop online stores to read, e.g. \['dmatcha'] or \['dmatcha', 'horii-shichimeien']. Only stores whose terms of use do not restrict this use are supported (d:matcha Kyoto, Horii Shichimeien, Yamamotoyama). Defaults to all 3 supported stores when omitted. Each store's whole public catalogue is read once per run (1–2 requests), and the filters below are applied afterwards.

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

Keep only these product types, e.g. \['matcha'] or \['matcha', 'gyokuro', 'hojicha']. The category is assigned from the product title/type in Japanese and English, so English category names work on Japanese-titled stores. Defaults to \['matcha'] when omitted; pass an empty list \[] to get every product including sweets, teaware and gifts. Sweets made with matcha are 'sweets', not 'matcha'; tea bags are 'teabag'.

## `keywords` (type: `array`):

Keep only products whose title, size, type, tags or SKU contain any of these words, e.g. \['奥ノ山'] or \['宇治', '玉露']. Store titles are Japanese; English or Chinese tea-type words (matcha→抹茶, sencha→煎茶, hojicha→ほうじ茶, 焙茶→ほうじ茶) are retried with a verified alias when they match nothing in a store. Defaults to no keyword filter. Combined with `categories` (both must match).

## `changeTypes` (type: `array`):

In 'changes' mode, return only these kinds of change, e.g. \['restocked'] for back-in-stock alerts or \['price_up', 'price_down'] for price tracking. A row matches if any of its changes is listed (a variant can be restocked and repriced at once). Defaults to all change types when empty. Ignored in 'snapshot' mode; changes filtered out here are still recorded in the state and are not reported again.

## `onlyAvailable` (type: `boolean`):

In 'snapshot' mode, drop variants that are currently sold out, e.g. to list only what can be ordered right now. Availability is the store's own in-stock flag for each size/variant. Defaults to false (sold-out variants are included with available=false). Ignored in 'changes' mode, where sell-outs are reported as 'sold_out' events.

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

Maximum rows for the whole run, e.g. 100 for a quick check or 1000 for every product in every store (about 510 variants in October 2026). Each row is one billable result, so this is your cost cap. Defaults to 1000 when omitted; accepts 1–20000. If a store's output is cut off, its state is not updated, so the missing changes are reported on the next run.

## `stateStoreName` (type: `string`):

Name of the key-value store in YOUR Apify account that keeps the previous run's prices and stock, e.g. 'japan-matcha-tea-monitor' or 'matcha-restock-alerts'. Letters, digits and hyphens only (max 63). Defaults to 'japan-matcha-tea-monitor'. Use a different name for each schedule with different filters so one monitor does not consume another's changes; a new name starts a new baseline.

## `autoRetryKeywordMapping` (type: `boolean`):

When a keyword matches nothing in a store, retry it once with a verified alias, e.g. 'matcha' → '抹茶' or 'hojicha' → 'ほうじ茶'. Only aliases checked against real store titles are used. Defaults to true. The run log and the RUN_REPORT record show which word was actually used for each store.

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

Proxy for reading the stores' public product feeds, e.g. {'useApifyProxy': true} (Apify datacenter proxy) or {'useApifyProxy': false} (direct). Shopify rate-limits per IP and has answered HTTP 429 to a bare cloud IP while allowing the datacenter proxy, so it is on by default. Defaults to Apify datacenter proxy; residential proxy is not needed. Requests go one at a time with pauses and automatic retries on 429.

## Actor input object example

```json
{
  "mode": "changes",
  "stores": [
    "dmatcha",
    "horii-shichimeien",
    "yamamotoyama"
  ],
  "categories": [
    "matcha"
  ],
  "keywords": [],
  "changeTypes": [],
  "onlyAvailable": false,
  "maxItems": 1000,
  "stateStoreName": "japan-matcha-tea-monitor",
  "autoRetryKeywordMapping": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `variants` (type: `string`):

Dataset items: store, title, size, SKU, price, in-stock flag, purchase-limit notice, URLs and changeType (restocked, sold_out, price_up, price_down, new_product, removed…). See the dataset schema.

## `runReport` (type: `string`):

Per-store summary: HTTP status, products/variants read, rows returned, change counts, whether the comparison state was updated, a status (ok, no_changes, no_match, partial, blocked_suspected, request_failed, skipped_limit) and a plain-language hint.

# 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": "changes",
    "stores": [
        "dmatcha",
        "horii-shichimeien",
        "yamamotoyama"
    ],
    "categories": [
        "matcha"
    ],
    "keywords": [],
    "changeTypes": [],
    "onlyAvailable": false,
    "maxItems": 1000,
    "stateStoreName": "japan-matcha-tea-monitor",
    "autoRetryKeywordMapping": true,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("datalab-jp/japan-matcha-tea-stock-monitor").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": "changes",
    "stores": [
        "dmatcha",
        "horii-shichimeien",
        "yamamotoyama",
    ],
    "categories": ["matcha"],
    "keywords": [],
    "changeTypes": [],
    "onlyAvailable": False,
    "maxItems": 1000,
    "stateStoreName": "japan-matcha-tea-monitor",
    "autoRetryKeywordMapping": True,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("datalab-jp/japan-matcha-tea-stock-monitor").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": "changes",
  "stores": [
    "dmatcha",
    "horii-shichimeien",
    "yamamotoyama"
  ],
  "categories": [
    "matcha"
  ],
  "keywords": [],
  "changeTypes": [],
  "onlyAvailable": false,
  "maxItems": 1000,
  "stateStoreName": "japan-matcha-tea-monitor",
  "autoRetryKeywordMapping": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call datalab-jp/japan-matcha-tea-stock-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datalab-jp/japan-matcha-tea-stock-monitor"
        }
    }
}
```

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/uN1pUvdf7dqbKg1C7/builds/6u4yvJL8dqCafdQF2/openapi.json
