# BOOTH VRChat 3D Products Scraper (`woolen_snake/booth-vrchat-3d-products`) Actor

Scrape BOOTH.pm VRChat 3D avatar and wearable listings — get price, shop info, images, and description as structured data.

- **URL**: https://apify.com/woolen\_snake/booth-vrchat-3d-products.md
- **Developed by:** [coco zizi](https://apify.com/woolen_snake) (community)
- **Categories:** E-commerce, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.00005 / actor start

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

## BOOTH VRChat 3D Products Scraper

An Apify Actor that scrapes structured product data from [BOOTH.pm](https://booth.pm) (pixiv's
marketplace for indie creators), targeting 3D avatars, outfits, and shaders made for VRChat. All-ages
products only (adult products are excluded).

### Features

- Scrape BOOTH.pm VRChat avatar and 3D model listings
- Extract price, shop, and image data from BOOTH products
- Filter BOOTH listings by keyword or category
- Fetch product descriptions and free-download file info
- Sync an entire BOOTH category across multiple runs

### What does this Actor do?

- Crawls a BOOTH product category (default: "3D Models") page by page and collects listing-level data
  (product name, price, shop info, thumbnail, like count)
- Optionally fetches additional detail-page data for each product (description, image list, free-
  download file info)
- Simple keyword filtering on product name (see "FAQ & known limitations" below for its constraints)
- Supports "full sync" across multiple runs to crawl an entire category (`startPage`)

### Who is this for?

This is a data feed for indie developers who want to work with BOOTH's product data programmatically —
for example:

- Building your own VRChat catalog or discovery site from BOOTH listings
- Tracking price and popularity (wishlist count) trends over time
- Feeding structured BOOTH product data to an AI agent or app that needs catalog info

### Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `categories` | array | `["3D Models"]` | BOOTH categories to crawl. VRChat avatars/outfits are concentrated mostly in "3D Models" |
| `sortBy` | string | `wish_lists` | Sort order: `wish_lists` (favorite count) / `popularity` / `new` (newest) |
| `onlyFreeProducts` | boolean | `false` | When on, restricts to free (¥0) products only |
| `keywordFilter` | string | `""` (empty) | Simple filter: keeps only products whose name contains this string. **Please read "FAQ & known limitations" below** |
| `maxItems` | integer | `60` | Cap on the number of products fetched in this run (max 1000). Drives run cost directly |
| `startPage` | integer | `1` | Pagination start page. Used when doing a full sync across an entire category (see below) |
| `fetchProductDetail` | boolean | `false` | When on, additionally fetches description, image list, and download info from each product page (extra charge per item). We recommend trying listing-only data first with this off |

### Output

Example output with `fetchProductDetail: true` (from a real, publicly listed product; only the key fields
are shown here — actual output also includes `productUrl`/`brand`/`shop`/`categoryId`/`categoryName`/
`liked`/`isAdult`/`thumbnailUrl`/`scrapedAt`/`description`/`images`). Product names naturally stay in the
seller's original language (mostly Japanese), since that's the actual listing data.

A paid product:

```json
{
  "productId": 5813187,
  "productName": "キプフェル Kipfel / オリジナル3Dモデル",
  "priceYen": 5500,
  "priceDisplay": "5,500 JPY",
  "downloadable": null
}
```

A free product (`downloadable` carries the distributed file info):

```json
{
  "productId": 3741802,
  "productName": "VR向けアバターモデル「RadDollV3」【無料】",
  "priceYen": 0,
  "priceDisplay": "0 JPY",
  "downloadable": [
    {
      "file_name": "RadDollV3_v3.02",
      "file_extension": ".zip",
      "file_size": "855 MB",
      "name": "RadDollV3_v3.02.zip",
      "url": "https://booth.pm/downloadables/7849637?variation_id=6226442"
    }
  ]
}
```

When `fetchProductDetail: false`, `categoryName` / `priceDisplay` / `description` / `images` /
`downloadable` are not included (all other fields are the same).

### How to fully sync an entire category

A single run can only fetch up to `maxItems` (max 1000). To fetch an entire category (e.g. "3D Models"
has over 260,000 listings), run this Actor multiple times, shifting `startPage` each time.

1. First run: use `startPage: 1` (default)
2. After the run, check the **`SYNC_STATE`** record saved to that run's key-value store (Apify Console →
   run details → Key-value store, or the `apify key-value-stores` command)
3. Check `SYNC_STATE.categories["<category name>"].nextStartPage` — `isExhausted: true` means that
   category is done; `false` means there's more to fetch
4. On subsequent runs, set `startPage` to that `nextStartPage` value. Repeat until `isExhausted: true`

**For full-sync use, specify only a single category in `categories`.** If you specify multiple categories
at once, the resume state for any category not yet reached when `maxItems` is hit won't carry over
correctly.

If a run stops mid-page because `maxItems` was reached, the rest of that page (up to 59 items) is skipped
on the next run rather than reprocessed. This can leave small coverage gaps, but guarantees no item is
ever charged twice across two sync runs.

### Pricing

Pay-per-event pricing:

| Event | Price | Trigger |
|---|---|---|
| Actor Start (`apify-actor-start`) | $0.00005 | Once per run (Apify's standard synthetic event; Apify covers the first 5 seconds of CU) |
| Product listed (`product-listed`) | $0.002 | Per product retrieved from the category listing |
| Product scraped (`product-detail-fetched`) | $0.03 | When `fetchProductDetail: true`, per product for which detail-page data (description, images, download info) was additionally fetched |

Example: fetching 100 items with `fetchProductDetail: true` ≈ $0.00005 + 100×$0.002 + 100×$0.03 ≈ $3.20

### FAQ

**Does this scrape adult (R-18) BOOTH products?**
No — only all-ages products are collected, based on the age flags on both the listing (`isAdult`) and
detail pages. Future changes to BOOTH's markup could cause this check to fail, so please verify sensitive
category flags yourself before using this for public-facing content.

**Can I get only VRChat-related items, not all of "3D Models"?**
BOOTH has no dedicated "VRChat" category — VRChat-related content makes up a large share of "3D Models",
but 3D assets for other platforms are mixed in too. `keywordFilter` does a simple string match against the
product name only (not BOOTH's own full-text search across tags/descriptions), so in practice
`keywordFilter: "VRChat"` only matches around 15-18% of what BOOTH's own search would return. Also note
that filtering by both category and keyword at once isn't supported while paginating, in the current
version, due to a dependency library constraint.

**How do I scrape an entire category, not just the first page?**
Use the `startPage`/`SYNC_STATE` full-sync flow described below — run this Actor repeatedly, feeding each
run's `SYNC_STATE.categories["<category name>"].nextStartPage` back in as the next run's `startPage`,
until `isExhausted: true`.

**What data do I get for each product?**
By default: product name, price, shop info, thumbnail, and like count. With `fetchProductDetail: true`,
you additionally get the description, image list, and free-download file info (see "Output" above).

**Will I be charged twice if a run is interrupted mid-page?**
No — the full-sync design guarantees no item is ever charged twice across two sync runs (it may leave
small coverage gaps instead of reprocessing).

# Actor input Schema

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

BOOTH categories to crawl. VRChat avatars/outfits are concentrated mostly in "3D Models".

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

Sort order for products within each category.

## `onlyFreeProducts` (type: `boolean`):

When on, only products priced at ¥0 are included.

## `keywordFilter` (type: `string`):

When set, only keeps products whose name contains this string (case-insensitive). Leave blank for no filtering. Note: unlike BOOTH's own tag search, this only checks the product name string, so a product with a matching tag but no matching text in its name will be excluded.

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

Cap on the number of products fetched across all categories combined. A charge event is generated per product, so this directly drives run cost.

## `startPage` (type: `integer`):

Pagination starts from this page number for each category. When crawling an entire category across multiple runs, set this to the nextStartPage value recorded in the previous run's output (SYNC\_STATE in the key-value store). Leave at 1 for a normal one-off run. For full-sync use, we recommend specifying only a single category in categories.

## `fetchProductDetail` (type: `boolean`):

When on, additionally fetches description, image list, and free-download file info from each product page, which aren't included in listing data (generates an extra charge event per product). We recommend trying listing-only data first with this off.

## Actor input object example

```json
{
  "categories": [
    "3D Models"
  ],
  "sortBy": "wish_lists",
  "onlyFreeProducts": false,
  "keywordFilter": "",
  "maxItems": 60,
  "startPage": 1,
  "fetchProductDetail": false
}
```

# Actor output Schema

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

Structured BOOTH product listings — name, price, shop, and, when "Fetch product detail" is enabled, description, images, and downloadable-file info.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("woolen_snake/booth-vrchat-3d-products").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("woolen_snake/booth-vrchat-3d-products").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 '{}' |
apify call woolen_snake/booth-vrchat-3d-products --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,woolen_snake/booth-vrchat-3d-products"
        }
    }
}

```

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/wdm6CxMb3e1Do0ZEm/builds/R1CpebEajXK1rLdBz/openapi.json
