# HobbyLink Japan (HLJ) Gunpla & Figures — Price + Stock Stats (`jpmarketdata/hlj-hobby-market-checker`) Actor

Asking-price stats for any hobby keyword on HobbyLink Japan: price quartiles (min/Q1/median/Q3/max), the availability split (in-stock / preorder / backordered / discontinued), on-sale share, median discount and release-year spread. Gunpla, kits, figures. From $0.02. ホビーリンクジャパンの模型相場を1コールで。

- **URL**: https://apify.com/jpmarketdata/hlj-hobby-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 keyword market 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

## HobbyLink Japan (HLJ) Gunpla & Figures — Price + Availability Stats in One Call

**What does this kit go for in Japan, and can you actually get it?** One call per keyword answers both — because for hobby goods the price alone is useless. A ¥4,500 Master Grade that is *discontinued* and a ¥4,500 Master Grade that is *in stock now* are not the same product to anyone who resells.

This Actor reads [HobbyLink Japan](https://www.hlj.com/) (HLJ — the Shizuoka hobby exporter that has shipped Gunpla, plastic model kits, figures, paints and tools worldwide since 1988) and returns **one compact market record per keyword**:

- **`priceJpy` — min / Q1 / median / Q3 / max** of the sampled listings, in yen, plus the same in USD at the current rate
- **`stockBreakdown`** — counts by HLJ's own state code: `instock` / `preorder` / `futurerelease` / `backorder` / `orderstop` / `discontinued`. This is the field that decides whether a keyword is a sourcing opportunity or a dead catalogue entry
- **`inStockRatioSampled`** and **`orderableRatioSampled`** — two different numbers on purpose, see the note below
- **`onSaleRatioSampled`** and **`saleRatePct`** — how much of the keyword is discounted, and by how much when it is
- **`releaseYearTop`** — the release-date spread, so you can see whether a keyword is current stock, a 2026 preorder wave or a back-catalogue tail
- Optionally every sampled item (title, price, list price, savings, discount %, stock state, release date, URL)

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

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

| Event | Price | When |
|---|---|---|
| Keyword market summary | **$0.02** | Per keyword analyzed |
| Individual item | **$0.002** | Only if you enable **Include individual listings** |

A default run (1 keyword, summary only) costs **$0.02**. **A keyword that returns zero listings is never charged.**

### Input

| Field | Example | Notes |
|---|---|---|
| `keywords` | `["RG Nu Gundam"]` | HLJ's catalogue is indexed in **English** — product and line names work best. Each keyword costs $0.02 |
| `pagesPerKeyword` | `2` | 1–8. One page = 24 items = 1 page request + 1 batch price request. Default 2 (48 items) |
| `includeIndividualItems` | `false` | Enable to also get each sampled item as its own record (+$0.002 each) |
| `convertToUsd` | `true` | Adds USD stats at the current exchange rate. A failed lookup never fails the run |

### What basis is each number on?

Every record carries **`priceJpyBasis`**, so you never have to guess:

| `priceJpyBasis` | What it means | When you get it |
|---|---|---|
| **`exact`** | The pages read covered the **whole result set** — these are true statistics over every matching listing | Narrow keywords (hits ≤ `pagesPerKeyword` × 24) |
| **`sample`** | Ordinary sample statistics over the listings actually read, `count` = how many of them had a price | Everything else |

**There is deliberately no third option.** HLJ's search returns a *relevance* order and offers no price sort, so there is no rank whose value is a population quantile. Sibling Actors of ours (`bookoff-market-checker`, `digimart-instrument-market-checker`) do read population quartiles — they can, because those platforms expose a monotonic price-ascending sort. HLJ does not, and this Actor **will not invent a population claim it cannot support**. What you get is an honest sample of the first *N* relevance-ranked hits, clearly labelled, with `totalListingsFound` next to it so you can see the sampling fraction yourself.

Everything else in the record (`stockBreakdown`, the ratios, `saleRatePct`, `releaseYearTop`) is likewise **sample-based** over `sampledListings`.

#### `inStockRatioSampled` vs `orderableRatioSampled` — read this one

HLJ's price feed carries an `is_in_stock` flag that is **`true` for backordered items too** — it means "you may place the order", not "it is on the shelf". Taken at face value it reports 100% availability on a keyword that is 38% backorder. So this Actor splits it:

- **`inStockRatioSampled`** — share of sampled items whose HLJ state code is literally `instock`. This is the one you want.
- **`orderableRatioSampled`** — share for which HLJ will accept an order at all (in-stock **plus** backorder/preorder). Reported separately, under a name that says what it is.

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

```json
{
  "type": "market_summary",
  "keyword": "RG Nu Gundam",
  "totalListingsFound": 273,
  "sampledListings": 24,
  "priceJpy": { "min": 500, "q1": 500, "median": 500, "q3": 4125, "max": 60000, "count": 24 },
  "priceJpyBasis": "sample",
  "pricedListings": 24,
  "stockBreakdown": { "instock": 15, "backorder": 9 },
  "inStockRatioSampled": 0.625,
  "orderableRatioSampled": 1.0,
  "onSaleRatioSampled": 0.0,
  "saleRatePct": null,
  "releaseYearTop": [[2025, 12], [2021, 7], [2026, 4], [2023, 1]],
  "checkedAt": "2026-08-01T05:33:51.318685+00:00",
  "sourceUrl": "https://www.hlj.com/search/?Word=RG+Nu+Gundam&page=1",
  "priceUsd": { "min": 3.15, "q1": 3.15, "median": 3.15, "q3": 26.02, "max": 378.48 },
  "exchangeRateJpyUsd": 0.006308
}
```

That ¥500 median is real and it is instructive: a `RG Nu Gundam` search matches the ¥500 water-slide decal sheets and detail parts *for* the kit as well as the ¥4,500 kit itself, and relevance order puts them near the top. **Search a full product name** (`RG 1/144 Nu Gundam`) when you want the kit's own price, and read `q3`/`max` alongside the median on any accessory-heavy keyword. The Actor reports what the search returned rather than quietly filtering to make the number look nicer.

### What this Actor does not do

- **No population quantiles.** HLJ exposes no price sort, so no rank can be claimed as a percentile of the whole result set — see the basis table
- **No sold prices.** HLJ is a shop: these are **asking** prices, the yen price a buyer would pay today, not completed-sale comps
- **No item dumps by default.** The product is the statistic; individual items are opt-in and separately priced
- **No login-only data.** Everything comes from public search pages and HLJ's own public price endpoint
- **No stored dataset.** Every run fetches live; nothing is retained between runs
- **No customer or seller PII.** HLJ is a single retailer — there are no third-party sellers to profile

### Notes on the data

- **Prices are not in the search HTML.** HLJ renders the result cards server-side but leaves the price span **empty** and fills it in with JavaScript. This Actor therefore pairs the card list with HLJ's own batch price endpoint (`/search/livePrice/?item_codes=…`, up to 40 codes per call) instead of running a browser — which is why it fits in 256 MB and finishes in seconds. A generic HTML scraper pointed at these pages returns titles with blank prices
- **Some items genuinely have no price** (unannounced or delisted products). They are still counted in `sampledListings` and in `stockBreakdown`; `pricedListings` tells you how many made it into the price statistics. They are never counted as ¥0
- **`priceJpy` is the sell price** — what a buyer pays today. When an item is discounted, the pre-discount price is available per item as `listPriceJpy`, with `savingsJpy` and `saleRatePct`
- **`saleRatePct` is the median over discounted items only.** Including the large majority of the catalogue that is not on sale would drag it to 0 and hide the only number a bargain hunter wants. `null` means nothing in the sample was on sale
- **`releaseYear`** is parsed from HLJ's `release_date` (e.g. `"July 2026"` → `2026`). Future years are normal and meaningful here: preorders for kits not yet released are a large part of this catalogue
- **Stock state codes are HLJ's own** (`instock`, `preorder`, `futurerelease`, `backorder`, `orderstop`, `discontinued`, …). Codes we do not recognise are passed through in `stockBreakdown` rather than dropped, so a new HLJ state never silently disappears from the split. Items HLJ's price feed did not answer for appear as `unpriced_or_unknown`
- Requests are throttled to ≥1.2 s and the run carries a wall-clock budget; if it is reached, later keywords are sampled over fewer pages and their records carry `truncatedForTimeLimit: true`

# Actor input Schema

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

One or more search keywords — HLJ's catalogue is indexed in English, so product and line names work best (RG Nu Gundam, MG Sazabi, Nendoroid, Tamiya 1/35, HGUC). Each keyword costs $0.02.

## `pagesPerKeyword` (type: `integer`):

How many search result pages to read per keyword. One page is 24 items and costs one page request plus one batch price request. HLJ offers no price sort, so this is an honest relevance-ordered sample, never a population quantile — every record says so via 'priceJpyBasis'. More pages = a larger sample and a longer run; 8 pages (192 items) is the maximum that fits the run's time budget.

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

Off by default: a run costs a flat $0.02 per keyword summary. Enable to also get every sampled item (title, price, list price, discount, in-stock flag, availability, release date, URL) at +$0.002 per item.

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

Adds USD statistics next to JPY using the current exchange rate (open.er-api.com). A failed rate lookup never fails the run — the JPY numbers are simply returned on their own.

## Actor input object example

```json
{
  "keywords": [
    "Gundam"
  ],
  "pagesPerKeyword": 2,
  "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 = {
    "keywords": [
        "Gundam"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jpmarketdata/hlj-hobby-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 = { "keywords": ["Gundam"] }

# Run the Actor and wait for it to finish
run = client.actor("jpmarketdata/hlj-hobby-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 '{
  "keywords": [
    "Gundam"
  ]
}' |
apify call jpmarketdata/hlj-hobby-market-checker --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/N7qj1tU2OMIYziuJi/builds/OX7uD0c3f7sgm2jVo/openapi.json
