# Tackleberry Used Fishing Tackle — Used vs New Population Split (`jpmarketdata/tackleberry-tackle-market-checker`) Actor

Used fishing-tackle market statistics from Tackleberry, Japan's 200-store used-tackle chain, in one call: the exact used / new / outlet population split reported by the shop itself, the used price quartiles and the used-to-new residual value ratio. From $0.02 per keyword, no subscription.

- **URL**: https://apify.com/jpmarketdata/tackleberry-tackle-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

## Tackleberry Japan Used Tackle Market — Used vs New Population Split

**How much Japanese fishing tackle is actually out there second-hand, and at what price?** One call per keyword answers it with **population counts reported by the shop itself** — not an estimate from the 60 listings a page happens to render.

This Actor reads **＠ベリーネット** ([b-net.tackleberry.co.jp](https://b-net.tackleberry.co.jp/)), the online arm of タックルベリー — Japan's 200-plus-store used fishing-tackle chain, ~122,000 items in stock, no login — and returns **one compact market record per keyword**:

- **`bySaleType` — the exact number of listings in each of the shop's three mutually exclusive stock classes.** Measured for `シマノ` on 2026-08-02: **34,397 listings** = 33,891 used + 454 new + 52 outlet. Those three add up to the unfiltered total *to the item*, and the Actor asserts it on every run (`saleTypeSplitConsistent`)
- **`priceJpy` per class** — for used gear the quartiles are read out of the **population** (¥2,311 / ¥9,240 / ¥16,748 / ¥30,204 / ¥277,200), not averaged off page one
- **`usedPriceCdf`** — a population price histogram built from the shop's own `price_max` counts: 3,047 used listings under ¥5,000, 9,024 under ¥10,000, 19,170 under ¥20,000, 30,951 under ¥50,000
- **`usedVsNew.medianRatio`** — the used median against the new median, published only when **both** are population figures
- **`sampleStats.conditionRankMix`** — the A / AB / B+ / B / B- / C grade spread of the sampled used gear
- Optionally every sampled listing (title, price, list price, discount, stock class, condition rank, URL)

The differentiator is the **stock split with real denominators**. Anyone can list fishing tackle; nobody on the Store can tell a JDM reseller that a brand has 33,891 used listings against 454 new ones, or that a quarter of the used stock sits under ¥10,000 — the numbers you need before deciding whether a model is worth sourcing for eBay or Mercari.

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

> **Note on the domain.** `www.tackleberry.co.jp` is the chain's store locator and carries no products. The shop is `b-net.tackleberry.co.jp`, which is what this Actor reads.

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

| Event | Price | When |
|---|---|---|
| Keyword market summary (`stats-computed`) | **$0.02** | Per keyword analyzed |
| Individual listing (`listing-returned`) | **$0.002** | Only if you enable **Include individual listings** |

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

### Input

| Field | Example | Notes |
|---|---|---|
| `keywords` | `["シマノ"]` | Brands or model numbers. Japanese works best — this is a Japanese shop. Each costs $0.02 |
| `saleTypes` | `[]` | Which stock classes to count. One request each. Empty = all three |
| `sortBy` | `"price_asc"` | `price_asc` / `price_desc` / `newest` / `rank`. Keep `price_asc` for population quantiles |
| `priceLadder` | `[]` | `price_max` rungs for the used CDF. One request each, max 8. Empty = 5000/10000/20000/50000 |
| `maxItemsPerKeyword` | `120` | Listings kept for the sampled blocks. Does **not** affect the counts or the quantiles |
| `includeIndividualItems` | `false` | Also emit each sampled listing (+$0.002 each) |
| `convertToUsd` | `true` | Adds USD stats at the current exchange rate |

### The one thing to understand: every number says where it came from

This Actor never lets a population figure and a page sample blur together. **Each block carries its own `basis`:**

| `basis` | Meaning | Where it is used |
|---|---|---|
| **`population`** | The shop's own hit count for a filter | `saleTypeCountsBasis` — the used / new / outlet counts |
| **`population_quantiles`** | The actual listed price at rank `ceil(N·q)`, read by jumping to the page that holds it | Used prices on a `price_asc` run |
| **`population_median`** | Min and median jumped out of the population; the IQR deliberately not computed | Classes over one page that do not justify four requests (the `new` class) |
| **`population_bands`** | Interpolated inside band counts that are themselves exact | `usedPriceCdf`, and the used-price fallback |
| **`exact`** | The whole class fit on the pages fetched, so the statistic *is* the population | The 52 outlet listings above |
| **`sample`** | Describes only the cards that were parsed | `sampleStats` (rank mix, discounts, new-arrival share) |

#### Why sort-and-jump rather than the count ladder — both were verified

Both population techniques work on this shop, which is unusual, so the choice is deliberate:

- **The price-ascending sort (`orderby=5`) is monotonic across pages.** Verified live on 2026-08-02 for the 33,891 used シマノ listings: pages 142 / 283 / 424 returned ¥9,240 / ¥16,748 / ¥30,204, and `price_max=2000` returned **0** listings, confirming that page 1's head (¥2,311) really is the population minimum.
- **The `price_max` ladder returns exact counts**, which makes a population histogram.

The rank-jump is used for the **headline quartiles** because it returns *prices that actually exist in the inventory* — an item you can go and look at — whereas the ladder interpolates inside a band. The gap is real and measurable: on the same population the ladder puts the median at **¥17,808** against the rank-jump's measured **¥16,748**, i.e. 6% high, because it has to assume prices are spread evenly inside the ¥10k–20k band and they are not.

The ladder is not wasted, though. It is published in its own right as `usedPriceCdf` (the shape of the distribution is a different product from its quartiles), and it is the **fallback**: if the monotonicity guard ever trips — a page jump the shop clamps, a probe whose values are not non-decreasing — the used prices fall back to `population_bands`, and only if that fails too, to `sample`. The claim always shrinks to what was actually measured.

### Output example (`type: "market_summary"`, real measured output)

```json
{
  "type": "market_summary",
  "keyword": "シマノ",
  "sortUsed": "price_asc",
  "sortCode": "5",
  "totalFound": 34397,
  "sampledListings": 120,
  "saleTypeCountsBasis": "population",
  "bySaleType": [
    { "code": "1", "key": "used",   "expectedLabel": "#中古品",      "labelSeen": "#中古品",      "labelMatchesCode": true,
      "count": 33891, "share": 0.9853, "sampledListings": 60,
      "priceJpy": { "min": 2311, "p25": 9240, "median": 16748, "p75": 30204, "max": 277200, "count": 33891 },
      "priceJpyBasis": "population_quantiles" },
    { "code": "2", "key": "new",    "expectedLabel": "#新品",        "labelSeen": "#新品",        "labelMatchesCode": true,
      "count": 454, "share": 0.0132, "sampledListings": 60,
      "priceJpy": { "min": 484, "p25": null, "median": 4488, "p75": null, "max": null, "count": 454 },
      "priceJpyBasis": "population_median" },
    { "code": "3", "key": "outlet", "expectedLabel": "#アウトレット", "labelSeen": "#アウトレット", "labelMatchesCode": true,
      "count": 52, "share": 0.0015, "sampledListings": 52,
      "priceJpy": { "min": 330, "p25": 880, "median": 1021, "p75": 1741, "max": 51590, "count": 52 },
      "priceJpyBasis": "exact" }
  ],
  "saleTypeSplitSum": 34397,
  "saleTypeSplitComplete": true,
  "saleTypeSplitConsistent": true,
  "usedPriceCdf": {
    "basis": "population_bands",
    "saleType": "used",
    "rungs": [
      { "upperBound": 5000,  "count": 3047,  "cumulativeShare": 0.0899 },
      { "upperBound": 10000, "count": 9024,  "cumulativeShare": 0.2663 },
      { "upperBound": 20000, "count": 19170, "cumulativeShare": 0.5656 },
      { "upperBound": 50000, "count": 30951, "cumulativeShare": 0.9133 }
    ],
    "bands": [
      { "from": 0,     "to": 5000,  "count": 3047 },
      { "from": 5000,  "to": 10000, "count": 5977 },
      { "from": 10000, "to": 20000, "count": 10146 },
      { "from": 20000, "to": 50000, "count": 11781 },
      { "from": 50000, "to": null,  "count": 2940 }
    ]
  },
  "usedVsNew": {
    "medianRatio": 3.7317,
    "basis": "population_quantiles/population_median",
    "note": "... catalogue-mix ratio, not a residual-value ratio ..."
  },
  "sampleStats": {
    "basis": "sample",
    "sampledListings": 120,
    "usedSampled": 120,
    "conditionRankMix": {
      "sampleSize": 120,
      "ranks": [
        { "code": "a",  "label": "A",  "count": 4,  "share": 0.0333 },
        { "code": "ab", "label": "AB", "count": 7,  "share": 0.0583 },
        { "code": "bp", "label": "B+", "count": 25, "share": 0.2083 },
        { "code": "b",  "label": "B",  "count": 34, "share": 0.2833 },
        { "code": "bm", "label": "B-", "count": 45, "share": 0.3750 },
        { "code": "c",  "label": "C",  "count": 5,  "share": 0.0417 }
      ]
    },
    "discountVsRegularPct": { "count": 0, "median": null },
    "newArrivalShare": 0.0333
  },
  "requestsMade": 13,
  "checkedAt": "2026-08-01T15:56:34.141774+00:00",
  "sourceUrl": "https://b-net.tackleberry.co.jp/products/list?name=%E3%82%B7%E3%83%9E%E3%83%8E&disp_number=60&orderby=5",
  "usedPriceUsd": { "min": 14.58, "p25": 58.29, "median": 105.65, "p75": 190.53, "max": 1748.58 },
  "exchangeRateJpyUsd": 0.006308
}
```

### What this Actor does not do

- **No sold prices.** These are current asking prices on a retailer's shelf. There is no transaction history on this shop and none is invented
- **No PII.** The shop is a company selling its own stock — there are no sellers, no accounts, no contact details. Nothing of that kind is collected or emitted
- **No `maker[]` filter.** It returns 1.4 MB in ~11 seconds; `name=` covers the same ground five times faster, so the Actor never sends it
- **No listing dumps by default.** The product is the statistic; individual listings are opt-in and separately priced
- **No stored dataset.** Every run fetches live; nothing is retained between runs
- **No browser, no proxy.** Plain HTTP, 256 MB

### Notes on the data

- **`usedVsNew.medianRatio` is a catalogue-mix ratio on a broad keyword.** The measurement above (3.73) does *not* mean used tackle costs 3.7× new tackle — it means that this shop's 454 new items are mostly accessories while its 33,891 used items include rods and reels. Pin the keyword to a single model and the same number becomes a genuine residual-value ratio. The `note` field in every record says so
- **Stock moves by the minute.** The unfiltered count read 34,401 / 34,399 / 34,397 in the space of a day, which is why every record carries `checkedAt` and why `saleTypeSplitConsistent` tolerates nothing but reports the gap rather than hiding it
- **`labelMatchesCode` is a tripwire, not decoration.** `sale_type[]=1` means "used" today; if the shop ever re-numbers its own filter, every statistic here would silently invert. The Actor therefore carries the shop's own `#中古品` / `#新品` / `#アウトレット` card tag next to the code it asked for and flags any disagreement
- **The condition grade lives in an icon filename** (`com-rank-bm-square.svg` = B-). There is no rank *filter*, so the grade mix can only ever be a sample — labelled as such — even though everything else here is a population figure
- **Used gear carries no list price**, so `discountVsRegularPct` is usually empty on a used-heavy keyword. Discounts are an outlet/new phenomenon on this shop, and reporting `count: 0` is the honest version of that
- **`sortBy` is always sent explicitly.** The shop's own default order is 新着順 (newest), which would skew every price statistic; the sort actually used is echoed back as `sortUsed`
- **Run it without a proxy.** Measured 2026-08-02 with the internal `dc-ip-probe` (runs `X1kho2Wzj37b74t0w`, `qBpVfzQB4pwpjjrM5`, `96imjNnaeywlMsdSd`): plain datacenter IPs get **200** on every combination of filter, sort and deep pagination. No anti-bot vendor is in front of it
- **The shop is slow** — 1.5–4 s per request typically, 4–12 s from a datacenter IP, with occasional 20 s+ outliers. A default run is 13 requests and took **68 s** end to end when this README was measured. A soft 85-second budget protects the 120-second target: if it is reached, the price ladder is cut from the tail first, the affected statistic drops to a weaker (still labelled) basis, and the record carries `truncatedForTimeLimit: true`. Run fewer keywords per call rather than raising the budget

# Actor input Schema

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

Brand names or model numbers to analyze, e.g. 'シマノ', 'ダイワ', 'メガバス', 'ステラ'. Japanese works best — this is a Japanese shop. Each keyword costs $0.02.

## `saleTypes` (type: `array`):

Which of the shop's three mutually exclusive stock classes to get exact counts for: used, new, outlet. Each one costs a request. Leave empty for all three — that split is what the product is about.

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

Which order the shop returns listings in. The shop's own default is 新着順 (newest), which would skew every price statistic, so this Actor always sends the sort explicitly and reports it as `sortUsed`. Keep 'price\_asc' for exact population quantiles — they are read by jumping to the rank of each quartile, which only works on a price-ascending sort. Any other value falls back to the price ladder (`population_bands`).

## `priceLadder` (type: `array`):

Upper bounds, in yen, for the used-price CDF. Each rung is one `price_max` search and returns an exact population count, so the histogram costs one cheap request per band edge and has no sampling error. Each rung costs a request; at most 8. Leave empty for 5000 / 10000 / 20000 / 50000.

## `maxItemsPerKeyword` (type: `integer`):

How many listings to keep per keyword for the condition-rank mix and the discount sample. The counts and the population quantiles are exact and are not affected by this. The sample is filled used-first from price-sorted pages, so 60 = the cheapest used page only, 120 also covers the lower-quartile page, 240 covers the whole distribution. Listings arrive 60 per page; this only caps how many are returned (and billed) when 'Include individual listings' is on (+$0.002 each).

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

Off by default: a run costs a flat $0.02 per keyword summary. Enable to also get every sampled listing (title, price, regular price, discount, stock class, condition rank, URL) at +$0.002 per listing.

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

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

## Actor input object example

```json
{
  "keywords": [
    "シマノ"
  ],
  "saleTypes": [],
  "sortBy": "price_asc",
  "priceLadder": [],
  "maxItemsPerKeyword": 120,
  "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": [
        "シマノ"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jpmarketdata/tackleberry-tackle-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": ["シマノ"] }

# Run the Actor and wait for it to finish
run = client.actor("jpmarketdata/tackleberry-tackle-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": [
    "シマノ"
  ]
}' |
apify call jpmarketdata/tackleberry-tackle-market-checker --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7agukre2FmADnTnZs/builds/95XTXvPZTcAzXfbdi/openapi.json
