# Gumroad Scraper · BOOTH & jinxxy Digital Product Research (`thequietstack/marketplace-demand-data`) Actor

Gumroad scraper for product research and market research: name a niche and get the digital Products already selling it on Gumroad, BOOTH and jinxxy, plus a summary per source with median price, demand sum and a review-based revenue floor. Empty searches are free.

- **URL**: https://apify.com/thequietstack/marketplace-demand-data.md
- **Developed by:** [TheQuietStack](https://apify.com/thequietstack) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 product records

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

## Gumroad Scraper · BOOTH & jinxxy Digital Product Research

**Gumroad scraper for digital product research,** plus BOOTH and jinxxy: what already sells in a niche, at which price, with reviews and wish lists.

**Digital product niche research in one run.** Type a niche ("notion template", "lightroom presets", "vrchat avatar")
and this Gumroad, BOOTH and jinxxy scraper returns the products already selling it on three creator marketplaces,
each with its price and the demand number that marketplace actually publishes. On top, a **niche summary** per
marketplace: how many products, median price per currency, demand sum, and, on Gumroad, a lower bound for
lifetime revenue.

Built for creators and product builders who want to know *before* they build: is anybody buying this, and at
what price?

### Use cases

- **Validate a product idea**: how many products exist for "notion budget template", what they cost, how many have buyers.
- **Price your product**: median and range per currency for your niche, from the live listings.
- **Find gaps across markets**: the same keyword on Gumroad (global), BOOTH (Japan) and jinxxy (VRChat).
- **Competitor tracking**: schedule a keyword list and watch new products and review counts arrive.
- **Affiliate and curation lists**: the top products by demand signal per niche, with links.

### What you get

| Marketplace | What it is | Demand signal | Notes |
|---|---|---|---|
| **Gumroad** | Global digital products | `reviewCount` (every review is a buyer) + `ratingAverage` | Also returns Gumroad's own tag vocabulary for the niche (`sourceTags`) |
| **BOOTH** (booth.pm) | Japan's creator market (pixiv) | `wishlistCount` | Prices in JPY. Also returns BOOTH's per-category counts for the keyword |
| **jinxxy** | VRChat avatars and assets | `likeCount` (a bookmark list, not sales) | Shows where the product is really sold (`salesChannel`: jinxxy, Payhip, Gumroad, ...) |

- **`null` means "not published", never 0.** BOOTH has no reviews, Gumroad has no wish lists: those fields are
  empty, so a sum or average never mixes a real 0 with a missing value.
- **Empty and failed are kept apart.** A search that loaded and found nothing is *empty*; a search that could not
  be loaded is *failed* (with the HTTP status). Neither is charged. Both are listed in the run summary.
- **Fair sample by default.** `sort: newest` reads the newest products first, so the summary describes the niche,
  not only its bestsellers. `mostDemand` reads the leaders instead.

### Input example

```json
{
    "keywords": ["notion template", "lightroom presets"],
    "sources": ["gumroad", "booth", "jinxxy"],
    "sort": "newest",
    "maxResultsPerSource": 20,
    "maxResults": 200,
    "boothWishlists": true,
    "maxCostUsd": "1.00"
}
```

### Output example (one row)

```json
{
    "keyword": "notion template",
    "source": "gumroad",
    "position": 1,
    "productId": "7XQWXb5l_Wr8EfUMriO_Mg==",
    "title": "the AI Portfolio System for Designers",
    "url": "https://6969634366200.gumroad.com/l/ai-portfolio-system",
    "sellerName": "Ioana Adriana Teleanu",
    "sellerUrl": "https://6969634366200.gumroad.com/",
    "price": 30,
    "currency": "EUR",
    "priceMinorUnits": 3000,
    "isFree": false,
    "isPayWhatYouWant": false,
    "recurrence": null,
    "reviewCount": 4,
    "ratingAverage": 5,
    "ratingScale": "1-5 stars",
    "wishlistCount": null,
    "likeCount": null,
    "demandSignal": 4,
    "demandSignalType": "reviews",
    "lifetimeRevenueFloor": 120,
    "category": "digital",
    "tags": null,
    "salesChannel": null,
    "publishedAt": null,
    "sortUsed": "hot_and_new",
    "scrapedAt": "2026-10-01T09:22:20.752Z"
}
```

### Niche summary (key-value store record `SUMMARY`)

One block per niche and marketplace, for example (live run 01.10.2026, "notion template", 10 per marketplace):

```json
{
    "keyword": "notion template",
    "source": "gumroad",
    "status": "ok",
    "totalAtSource": 229,
    "productsInSample": 10,
    "priceByCurrency": {
        "EUR": { "products": 2, "medianPrice": 16.995, "minPrice": 3.99, "maxPrice": 30, "freeProducts": 0 },
        "USD": { "products": 8, "medianPrice": 21.35, "minPrice": 3.99, "maxPrice": 79, "freeProducts": 0 }
    },
    "demandSignalType": "reviews",
    "demandSignalSum": 21,
    "lifetimeRevenueFloorByCurrency": { "EUR": 123.99, "USD": 123.08 },
    "lifetimeRevenueFloorNote": "LOWER BOUND, not an estimate: reviews x current list price. ..."
}
```

**About `lifetimeRevenueFloor`:** reviews × current list price. Every Gumroad review comes from a buyer, so the
real revenue is at least this much; buyers who did not review, discounts, refunds, price changes and taxes are
not in it. It is a floor, not an estimate, and it is summed per currency, never converted. It is not computed
for BOOTH (wish lists) and jinxxy (likes), because neither proves a purchase.

Other summary fields: `sourceTags` (Gumroad), `sourceCategories` (BOOTH), `salesChannels` and `pagesAtSource`
(jinxxy), `topByDemandSignal`, `failed`, `empty`, `duplicatesSkipped`, `looseMatchesDropped`,
`boothWishlistFailures`, `http`, `billing`.

### How much does it cost?

Pay per event. Platform usage is included.

| Event | Charged when | Price |
|---|---|---|
| `product-record` | one product row is written | $0.004 |
| `niche-report` | once per keyword that returned at least one product (the niche summary) | $0.02 |
| Actor start | once per run | $0.00005 |

Examples:

- **1,000 product rows** = **$4**.
- **One niche on all three marketplaces**, 20 products each (60 rows) + 1 niche report = $0.24 + $0.02 = **$0.26**.
- **10 niches on Gumroad only**, 20 products each = 200 rows + 10 reports = $0.80 + $0.20 = **$1.00**.
- A keyword with no products anywhere = **$0**.

Empty searches, failed searches, duplicates and loose matches cost nothing. Your platform "Max cost per run" is
respected, and `maxCostUsd` stops the run before the first row it could not pay for, using the live prices.

### Limits and honesty

- **Gumroad** returns 9 products per request; with `newest` the list moves while new products arrive, so a product
  can repeat on the next page (removed as a duplicate, not charged) or be skipped. `totalAtSource` is Gumroad's
  own count. Gumroad does not publish a sales count or a publish date in search; those fields are `null`.
- **BOOTH** shows prices in JPY. The wish list count needs one extra request per product (paced at 1 per second);
  if that request fails, `wishlistCount` is `null` and the product is listed in `boothWishlistFailures`. BOOTH's
  item data also contains a field called `past_purchase_count`: it is the *visitor's own* purchase count (always 0
  without login), **not** a sales count, and is deliberately not used. With 0 results BOOTH shows suggestions for
  parts of the keyword; those are not results and are not returned.
- **jinxxy** matches loosely (a made-up keyword returned 4 products in testing). Only products whose name contains
  every word of your keyword are kept; the others are counted in `looseMatchesDropped`. jinxxy ignores sort
  options and has no exact result count (`totalAtSource: null`, `pagesAtSource` instead). Reviews exist only for
  products sold on jinxxy itself; for products that link out, `reviewCount` is `null`.
- **Not included, on purpose:** Patreon (its search answers 403 to non-browser clients), DISBOARD, itch.io,
  Payhip marketplace, cults3d (behind bot protection), anything behind a login, prediction markets.
- Polite by design: at most one request per second per marketplace, honest User-Agent, no browser disguise,
  no bot-protection workarounds.

### Use it via API, integrations and AI agents

```bash
curl -X POST "https://api.apify.com/v2/acts/thequietstack~marketplace-demand-data/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["notion template"],"sources":["gumroad"],"maxResultsPerSource":20}'
```

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("thequietstack/marketplace-demand-data").call(run_input={
    "keywords": ["lightroom presets", "notion template"],
    "sources": ["gumroad", "booth", "jinxxy"], "maxResultsPerSource": 20,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["source"], row["title"], row["price"], row["currency"], row["demandSignal"])
summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("SUMMARY")["value"]
```

- **Export**: JSON, CSV or Excel from the dataset; the niche summary is the `SUMMARY` record.
- **Integrations**: Google Sheets, Make, Zapier, n8n and webhooks via the integrations tab.
- **AI agents (MCP)**: `https://mcp.apify.com?tools=thequietstack/marketplace-demand-data`.

### FAQ

**Does it show Gumroad sales numbers?** No. Gumroad does not publish sales counts in search. The Actor returns the review
count (every review is a buyer) and computes `lifetimeRevenueFloor` = reviews x current price, a lower bound, not an estimate.

**Why are BOOTH prices in yen?** BOOTH lists in JPY. Prices are never converted; summaries are per currency.

**Is a jinxxy like a sale?** No. It is a bookmark. That is why `demandSignalType` names the signal per row.

**Why do some rows have `null` instead of 0?** `null` means the marketplace does not publish that number. A real 0 is 0.

**Does it need a login or proxies?** No. Only public pages that every visitor sees, about one request per second per
marketplace, no bot-protection workarounds.

**Which sort should I use?** `newest` (default) gives a fair sample of the niche; `mostDemand` shows the leaders.

**Is Patreon included?** No. Patreon's search answers 403 to non-browser clients, and that is not worked around.

### Data sources, terms and legal

- Data comes from the **public** pages and endpoints that each marketplace serves to any visitor without login:
  `gumroad.com/products/search` (the JSON behind Gumroad Discover), `booth.pm/en/search/...` and
  `booth.pm/en/items/<id>.json`, `jinxxy.com/market/browse`. robots.txt was checked on 01.10.2026; none of these
  paths is disallowed (Gumroad disallows `/purchases/`, BOOTH `/terms` and carts, jinxxy `/api/`, `/my/`, carts —
  none of which is used).
- Product data stays the property of the marketplaces and their sellers: Gumroad (Gumroad, Inc.), BOOTH
  (pixiv Inc.), jinxxy. Attribute them when you publish results, and respect each marketplace's terms of service
  for any further use. This Actor does not grant any license to product images, descriptions or files.
- **Personal data:** the only personal data returned is what the marketplace itself shows publicly next to a
  product: the seller's display name and shop/profile URL. No emails, no buyer data, no contact details. If you
  process this data in the EU/EEA, you are the controller under the GDPR: have a lawful basis (typically
  legitimate interest for market research), keep it to what you need, and do not use it for unsolicited
  marketing.
- This Actor is not affiliated with, endorsed by or sponsored by Gumroad, pixiv/BOOTH or jinxxy.

# Actor input Schema

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

One niche per line, as a buyer would search it, e.g. <code>notion template</code>, <code>lightroom presets</code>, <code>vrchat avatar</code>. Duplicates (case-insensitive) are removed.

## `sources` (type: `array`):

Empty = all three. <b>gumroad</b>: global digital products, demand = reviews (each review is a buyer). <b>booth</b>: Japanese creator market (pixiv), prices in JPY, demand = wish lists (one extra request per product). <b>jinxxy</b>: VRChat assets, demand = likes; only products whose name contains every word of your keyword are kept.

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

<b>newest</b> (recommended for comparing niches): newest products first, so the summary describes the niche, not only its winners. <b>mostDemand</b>: most reviewed (Gumroad) / most wish-listed (BOOTH) first, to study the leaders. jinxxy always returns its own default order.

## `maxResultsPerSource` (type: `integer`):

Hard limit per niche and marketplace (1 to 300).

## `maxResults` (type: `integer`):

Hard limit for the whole run, across all niches and marketplaces.

## `boothWishlists` (type: `boolean`):

On: one extra request per BOOTH product for its wish list count (the only demand number BOOTH publishes). Off: faster, wishlistCount stays null.

## `maxCostUsd` (type: `string`):

Optional. The run stops before any product it could not pay for under this amount, using the live per-event prices. Empty = only the platform's "Max cost per run" applies.

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

Optional. All three marketplaces answered without a proxy when this Actor was built.

## Actor input object example

```json
{
  "keywords": [
    "notion template"
  ],
  "sources": [
    "gumroad",
    "booth",
    "jinxxy"
  ],
  "sort": "newest",
  "maxResultsPerSource": 20,
  "maxResults": 1000,
  "boothWishlists": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "keywords": [
        "notion template"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thequietstack/marketplace-demand-data").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": ["notion template"] }

# Run the Actor and wait for it to finish
run = client.actor("thequietstack/marketplace-demand-data").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 '{
  "keywords": [
    "notion template"
  ]
}' |
apify call thequietstack/marketplace-demand-data --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thequietstack/marketplace-demand-data"
        }
    }
}
```

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/lCR58tBhy5xmPSn2o/builds/wmschCCYB13Rui0f3/openapi.json
