# Whatnot Marketplace Scraper & Monitor (`luminar/whatnot-marketplace-scraper-monitor`) Actor

Search public Whatnot listings, sellers, categories and shows, inspect listing details, or track reliable marketplace changes with stable IDs and explicit coverage.

- **URL**: https://apify.com/luminar/whatnot-marketplace-scraper-monitor.md
- **Developed by:** [Luka](https://apify.com/luminar) (community)
- **Categories:** E-commerce, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 whatnot marketplace 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?

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

Collect public Whatnot marketplace data without building your own crawler. Search products and live shows, inspect seller activity, open exact listings, or run the same scope repeatedly to receive reliable `NEW`, `UPDATED`, and confirmed `ENDED` changes.

The default run is deliberately simple: `pokemon` product search, core fields, and at most 100 rows. Every result has a stable ID and public URL, while every run explains whether its coverage was complete, capped, partial, blocked, failed, or a confirmed empty result.

### 🚀 Start in 60 seconds

1. Leave **What do you want to collect?** on **Search the marketplace**.
2. Replace `pokemon` with your keyword, or keep it for the first run.
3. Set **Maximum result rows** to the number you need.
4. Click **Start**.
5. Open **Marketplace records** for the clean table or **Run summary** for coverage, field-completeness and billing totals.

Cheapest useful input:

```json
{
  "workflow": "search",
  "searchQueries": ["pokemon"],
  "vertical": "PRODUCT",
  "detailLevel": "core",
  "maxResults": 25,
  "maxResultsPerTarget": 25
}
```

### 🎯 Choose the right workflow

| Workflow | Use it when you need | Typical output |
|---|---|---|
| **Search the marketplace** | Products, live shows, sellers or categories matching keywords or a public Whatnot search URL | Flat listing, livestream, seller and category rows |
| **Seller profile and shop** | A public seller snapshot and the shop/show links visible on the profile | Seller metrics plus requested public child rows |
| **Category and shows** | A public category page and the current activity visible there | Category, livestream and public seller rows |
| **Exact listing details** | One or more known public listing URLs | Detailed listing rows with extra fields where published |
| **Track changes** | Repeat the same search, seller, category or listing scope | `NEW`, `UPDATED` and two-scan-confirmed `ENDED` rows |

For Search, start with `PRODUCT`. Choose `LIVESTREAM`, `USER`, `CATEGORY`, or `UNIVERSAL` only when you need those public result types. For monitoring, schedule identical input and keep the same **Monitoring scope name**.

### 📦 What you get

All marketplace rows share the same dependable envelope:

| Field | Meaning |
|---|---|
| `recordType` | `listing`, `livestream`, `seller`, `category`, `change`, or `coverage` |
| `stableId` / `sourceId` | Joinable Whatnot-derived identity that remains stable across repeat runs |
| `url` | Canonical public Whatnot page |
| `title` | Public listing, show, seller or category title |
| `sellerUsername` / `sellerUrl` | Public seller identity when the source associates one |
| `price` / `currency` | Numeric displayed price and ISO currency |
| `quantity`, `currentBid`, `bidCount` | Public commerce fields when shown |
| `listingStatus` | Public listing or livestream status |
| `imageUrls` | Public image gallery available on the selected surface |
| `rating`, `reviewCount`, `followerCount`, `soldCount` | Public seller metrics when shown |
| `activeViewers`, `startsAt`, `categoryName`, `rank` | Public show/category signals when shown |
| `sourceCreatedAt`, `sourceUpdatedAt` | Source-published timestamps when shown |
| `observedAt` | UTC observation time |

Example listing row:

```json
{
  "recordType": "listing",
  "stableId": "whatnot:listing:TGlzdGluZ05vZGU6MTk2MTc5Mjg1Mw==",
  "sourceId": "TGlzdGluZ05vZGU6MTk2MTc5Mjg1Mw==",
  "url": "https://www.whatnot.com/listing/TGlzdGluZ05vZGU6MTk2MTc5Mjg1Mw%3D%3D",
  "title": "Pokemon collector item",
  "sellerUsername": "public_seller",
  "price": 40,
  "currency": "USD",
  "quantity": null,
  "listingStatus": "PUBLIC",
  "observedAt": "2026-09-03T00:00:00.000Z",
  "sourceClass": "PUBLIC_FIRST_PARTY",
  "sourceName": "Whatnot"
}
```

Nullable fields are honest: `null` means the selected public Whatnot surface did not publish that field. It is never replaced with a guess.

![Actual Whatnot marketplace rows with stable IDs, sellers, prices, public URLs and explicit coverage](https://api.apify.com/v2/key-value-stores/DJgPR6wwS2tIaLDQW/records/whatnot_marketplace_scraper_monitor--4ed99a7a0ba19965-marketplace-dataset.png)

### 🎛️ Input guide

- **Search keywords / Whatnot search URLs** — use one or combine several; duplicate records are merged by stable ID.
- **Seller usernames** — public handles only; `@` is accepted.
- **Category URLs** — public `/tag/` or `/category/` pages.
- **Listing URLs** — exact public `/listing/` pages.
- **Listing detail level** — Core is the fast, low-cost default. Detailed opens every product listing page and uses its own event price.
- **Include public category sellers** — adds seller rows associated with shows on a category page; no unsupported rank is inferred.
- **Maximum result rows** — hard run-wide cap, from 1 to 100.
- **Maximum rows per target** — keeps one large target from consuming the entire run.
- **Maximum loading rounds** — bounds infinite loading; a reached bound is reported as `CAPPED`.
- **Maximum charge for this run** — preflight refusal when the active worst-case event bill would exceed your limit.

The Product input contains no cookie, login, token, proxy, session, diagnostic, or fault controls.

### 💰 Pricing

Pay only when at least one target returns trustworthy work, plus delivered rows. Fully failed or blocked runs have no source/result charge; coverage rows are always free.

| Event | Free | Bronze | Silver | Gold / Platinum / Diamond |
|---|---:|---:|---:|---:|
| Verified run source | $0.0120 | $0.0120 | $0.0120 | $0.0120 |
| Marketplace row | **$0.80 / 1,000** | **$0.75 / 1,000** | **$0.70 / 1,000** | **$0.60 / 1,000** |
| Detailed listing row | $8.00 / 1,000 | $8.00 / 1,000 | $8.00 / 1,000 | $8.00 / 1,000 |
| Comparable monitoring run | $0.0500 | $0.0500 | $0.0500 | $0.0500 |
| Delivered change | **$0.80 / 1,000** | **$0.75 / 1,000** | **$0.70 / 1,000** | **$0.60 / 1,000** |

A 100-row core run is $0.092 on Free and $0.072 on Gold before any plan-specific Apify platform costs. The leading broad competitor's captured active row price is about $0.30 for 100 rows on Free and $0.10 on Gold, including its start event. On the measured 71-row representative workload, our bill is $0.0688 on Free and $0.0546 on Gold, versus about $0.2131 and $0.0711 for the leader.

The fixed source event is charged once per successful run, not once per query. It covers source access while every delivered core row stays below the leader's per-row price. The small-run crossover is transparent: our total becomes lower at 6 rows on Free, 10 on Bronze, 15 on Silver, and 30 on Gold/Platinum/Diamond. Below those points, the leader can be cheaper; from those points onward, this Actor is cheaper for the comparable core row.

Detailed mode is intentionally separate and capped at 25 rows: one public page is opened per listing, so its cost must not be hidden inside the low marketplace-row price.

Normal scraping fails a target closed when the public result window does not provide query-bound stable IDs; it never silently switches to a materially more expensive scope. Monitoring uses the stricter complete-scan path needed to evaluate absence, which is why its fixed event is priced separately.

### ✅ Coverage you can trust

Every run writes one free coverage row:

- `COMPLETE` — every target reached a stable public result window within the limits.
- `EMPTY_CONFIRMED` — the expected result surface loaded and explicitly confirmed no matches.
- `CAPPED` — a declared limit stopped the walk; useful rows are returned, but the result is not called complete.
- `PARTIAL` — some targets completed while another failed or was blocked.
- `BLOCKED` — public source access was blocked; this is never disguised as empty.
- `FAILED` — the source or parser could not establish a trustworthy result.

Monitoring state changes only after paid delivery is acknowledged and only on trustworthy coverage. A listing missing once stays active with one absence. It becomes `ENDED` only after a second comparable complete scan also misses it. `FAILED`, `PARTIAL`, `CAPPED`, or `BLOCKED` runs never end unseen records and never advance the baseline.

The first Monitor run defaults to **Create baseline without NEW rows**, preventing an expensive first-run change flood. Choose **Return current rows as NEW** only when that is the outcome you want.

### 🔌 API and automation

Run from JavaScript:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('luminar/whatnot-marketplace-scraper-monitor').call({
  workflow: 'search',
  searchQueries: ['pokemon'],
  vertical: 'PRODUCT',
  detailLevel: 'core',
  maxResults: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

For monitoring, use one saved Task with identical input and a sequential schedule. Read `OUTPUT` first: automate downstream actions only when `coverageStatus` is `COMPLETE` or when your workflow explicitly accepts a capped/partial dataset.

Exports work through Apify Dataset in JSON, CSV, Excel, XML and other supported formats.

### ⚠️ Not yet supported

- Login-only or personalized data, private messages/contacts, orders, inventory management, imported account cookies/tokens, or CAPTCHA solving.
- Guaranteed population of fields Whatnot does not publish on a selected public page.
- Historical sold-price estimates, investment scores, sentiment scores, alerts, or outbound messaging.
- Concurrent runs that write the same monitoring namespace; schedule one scope sequentially.
- More than four targets or 100 result rows in one run. Split a larger job into separate saved Tasks.

### ❓ FAQ and support

**Why is a field null?**\
Whatnot publishes different fields for listings, shows, profiles and categories. The Actor preserves that distinction instead of inferring missing values.

**Why did I receive fewer rows than requested?**\
Open the coverage row. The public scope may have ended naturally, explicitly returned no matches, or hit a declared cap.

**Does one missing listing mean it ended?**\
No. `ENDED` requires two complete comparable misses. Anything less remains active or not observed.

**Why does Detailed cost more?**\
Core search reuses the marketplace result surface. Detailed opens each listing page separately for additional public fields.

**Can I use several targets?**\
Yes, within the input limits. Duplicate stable IDs are merged and not billed twice.

For support, include the run ID, workflow, coverage status and a public target URL. Never send account cookies, passwords or private tokens.

# Actor input Schema

## `workflow` (type: `string`):

Search finds public marketplace records; Seller reads public profiles and shop/show links; Category reads public category activity; Listing opens exact public listings; Monitor compares repeat runs safely.

## `searchQueries` (type: `array`):

One or more marketplace searches, for example pokemon, vintage comics, or sneakers. Used by Search and search-based Monitor.

## `searchUrls` (type: `array`):

Optional full public Whatnot search URLs. URL filters and vertical are preserved.

## `vertical` (type: `string`):

Choose which public Whatnot result type a keyword search should return.

## `sellerUsernames` (type: `array`):

Public Whatnot handles for Seller or seller-based Monitor. Leading @ is accepted.

## `categoryUrls` (type: `array`):

Public Whatnot tag or category URLs, for example a Pokémon Cards category page.

## `listingUrls` (type: `array`):

Exact public Whatnot listing URLs for detail collection or exact-listing Monitor.

## `includeShop` (type: `boolean`):

Include public listing rows visible on Seller pages.

## `includeShows` (type: `boolean`):

Include live or upcoming public show rows visible on Seller and Category pages.

## `includeCategorySellers` (type: `boolean`):

Include public seller rows associated with shows on the selected category page. No unsupported rank is inferred.

## `detailLevel` (type: `string`):

Detailed is available for Product search and Exact listing workflows, with at most 25 result rows per run. It can add description, quantity, seller link, images and source creation date where Whatnot publishes them.

## `monitorType` (type: `string`):

Used only by Track changes. Choose which target fields define the baseline.

## `firstRunBehavior` (type: `string`):

Seed-only avoids a first-run change flood. Emit-current returns all first observations as paid NEW events.

## `stateNamespace` (type: `string`):

Separates independent baselines. Keep the same name and exact input for repeat runs.

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

Hard cap across all targets. Use another Task when you need a separate additional scope.

## `maxResultsPerTarget` (type: `integer`):

Stops one large query, seller or category from consuming the whole run.

## `maxScrollRounds` (type: `integer`):

Bounds infinite-scroll collection. Reaching this cap produces CAPPED coverage.

## `maxBuyerChargeUsd` (type: `number`):

The Actor refuses to start when the active event-price worst case exceeds this amount.

## Actor input object example

```json
{
  "workflow": "search",
  "searchQueries": [
    "pokemon"
  ],
  "vertical": "PRODUCT",
  "includeShop": true,
  "includeShows": true,
  "includeCategorySellers": true,
  "detailLevel": "core",
  "monitorType": "search",
  "firstRunBehavior": "seed_only",
  "stateNamespace": "default",
  "maxResults": 100,
  "maxResultsPerTarget": 100,
  "maxScrollRounds": 12,
  "maxBuyerChargeUsd": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

All marketplace, change and coverage rows from this run.

## `marketplace` (type: `string`):

Listings, shows, sellers and categories with their public buyer fields.

## `changes` (type: `string`):

NEW, UPDATED and ENDED monitoring rows with previous and current values.

## `coverage` (type: `string`):

What the run reached, whether a limit stopped it and whether absence was comparable.

## `runSummary` (type: `string`):

Counts, field coverage, limits, billing events and the recommended next action.

# 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 = {
    "workflow": "search",
    "searchQueries": [
        "pokemon"
    ],
    "vertical": "PRODUCT",
    "includeShop": true,
    "includeShows": true,
    "includeCategorySellers": true,
    "detailLevel": "core",
    "monitorType": "search",
    "firstRunBehavior": "seed_only",
    "stateNamespace": "default",
    "maxResults": 100,
    "maxResultsPerTarget": 100,
    "maxScrollRounds": 12,
    "maxBuyerChargeUsd": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("luminar/whatnot-marketplace-scraper-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 = {
    "workflow": "search",
    "searchQueries": ["pokemon"],
    "vertical": "PRODUCT",
    "includeShop": True,
    "includeShows": True,
    "includeCategorySellers": True,
    "detailLevel": "core",
    "monitorType": "search",
    "firstRunBehavior": "seed_only",
    "stateNamespace": "default",
    "maxResults": 100,
    "maxResultsPerTarget": 100,
    "maxScrollRounds": 12,
    "maxBuyerChargeUsd": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("luminar/whatnot-marketplace-scraper-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 '{
  "workflow": "search",
  "searchQueries": [
    "pokemon"
  ],
  "vertical": "PRODUCT",
  "includeShop": true,
  "includeShows": true,
  "includeCategorySellers": true,
  "detailLevel": "core",
  "monitorType": "search",
  "firstRunBehavior": "seed_only",
  "stateNamespace": "default",
  "maxResults": 100,
  "maxResultsPerTarget": 100,
  "maxScrollRounds": 12,
  "maxBuyerChargeUsd": 10
}' |
apify call luminar/whatnot-marketplace-scraper-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,luminar/whatnot-marketplace-scraper-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/9pdiIQt9I9EnElK8Y/builds/uVVevMh8XkyJxN9cu/openapi.json
