# TCGplayer Price Monitor (`sellavir/tcgplayer-price-monitor`) Actor

Monitor TCGplayer market prices and listed medians per product/printing, with run-over-run change detection. Pure JSON API, no browser.

- **URL**: https://apify.com/sellavir/tcgplayer-price-monitor.md
- **Developed by:** [Vic](https://apify.com/sellavir) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/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

## TCGplayer Price Monitor

Monitors TCGplayer **market price** and **listed median price** for trading-card
products (Pokemon, Magic: The Gathering, Yu-Gi-Oh!, Lorcana, One Piece, and
more) — one row per product x printing (Normal / Foil / 1st Edition ...), with
optional **run-over-run change detection** so you only react to prices that
actually moved.

Pure JSON-over-HTTP (no browser), built for cheap scheduled monitoring runs.

### Quickstart (3 lines)

1. Enter one or more **search queries** (e.g. `charizard ex obsidian flames`) *or* paste known **product IDs / product URLs** (e.g. `610532` or `https://www.tcgplayer.com/product/610532`).
2. Run the Actor — each product yields one dataset row per printing with `marketPrice` and `listedMedianPrice`.
3. Schedule it (e.g. daily) with a fixed `productIds` list: with `enableChangeDetection` on, every row carries `priceChanged`, `prevMarketPrice`, and `marketPriceDelta` versus your previous run.

### What it does

- Resolves search queries via TCGplayer's product search (paginated, capped by `maxResultsPerQuery`).
- Accepts explicit `productIds` (skips search; metadata is resolved in one batched lookup) — the reliable mode for scheduled monitoring.
- Fetches per-printing price points for every unique product: `marketPrice`, `listedMedianPrice`, `buylistMarketPrice`.
- Joins in search metadata: set name, rarity, card number, lowest listed price, listing count.
- Change detection: keeps a snapshot of your last run in a named key-value store (`tcgplayer-price-monitor-state`) and emits `priceChanged` / `prevMarketPrice` / `prevListedMedianPrice` / `marketPriceDelta` / `changedAt`.
- Contract test on every run: a known-good product's response shape is validated; on upstream schema drift the run warns loudly (status message + one uncharged `warning` row) instead of silently emitting garbage.

### What it does NOT do

- No seller-level listings or seller-filtered pages (those are disallowed by tcgplayer.com robots.txt — we do not touch them).
- No buying, sniping, or cart automation.
- No historical backfill — price history accrues from your own scheduled runs.
- No fabricated values: if TCGplayer has no market price for a printing, you get `null` (a price of 0 is also treated as "no price").

### Input examples

One-shot search:

```json
{
    "queries": ["charizard ex obsidian flames"],
    "productLine": "pokemon",
    "maxResultsPerQuery": 25
}
```

Scheduled daily monitor of a fixed list (recommended):

```json
{
    "productIds": ["610532", "113670", "https://www.tcgplayer.com/product/477236"],
    "printings": ["Foil"],
    "enableChangeDetection": true
}
```

### Output example (real rows from a test run, 2026-08-29)

```json
{
    "productId": 610532,
    "productName": "Teal Mask Ogerpon ex - 177/131",
    "setName": "SV: Prismatic Evolutions",
    "productLine": "Pokemon",
    "rarity": "Hyper Rare",
    "number": "177/131",
    "printingType": "Foil",
    "marketPrice": 8.79,
    "listedMedianPrice": 9.86,
    "buylistMarketPrice": null,
    "lowestPrice": 6.78,
    "listingCount": 189,
    "sealed": false,
    "url": "https://www.tcgplayer.com/product/610532",
    "scrapedAt": "2026-08-29T14:32:23.077Z",
    "prevMarketPrice": null,
    "prevListedMedianPrice": null,
    "marketPriceDelta": null,
    "priceChanged": false,
    "changedAt": null
}
```

On a later run, after the market price moved:

```json
{
    "productId": 610532,
    "printingType": "Foil",
    "marketPrice": 8.79,
    "prevMarketPrice": 7.50,
    "marketPriceDelta": 1.29,
    "priceChanged": true,
    "changedAt": "2026-08-29T14:33:16.634Z"
}
```

(second example abbreviated; all rows always contain the full field set)

Notes on fields:

- `priceChanged` is `true` when `marketPrice` *or* `listedMedianPrice` differs from the previous snapshot for that product+printing.
- On the very first run there is no previous snapshot, so all rows have `priceChanged: false` and `prev*: null`.
- `productTypeName` is emitted for completeness but TCGplayer's search payload currently does not populate it; use the `sealed` boolean to separate sealed products from singles.

### Pricing (pay per event)

| Event | Price | Charged when |
|---|---|---|
| Run start | $0.001 | once per run |
| Product price row | $0.003 | for every product x printing row delivered to the dataset |
| Price change row | $0.004 (additional) | only for delivered rows with `priceChanged: true` |

Failed fetches, filtered-out printings, nonexistent/unknown product IDs, and the schema-drift warning row are never charged.

Worked example: monitoring 500 rows daily (a row = one product x printing; use
the `printings` filter to get exactly one row per product) where ~10% move per
day costs about 500 x $0.003 + 50 x $0.004 + $0.001 ~= **$1.70/day
(~$51/month)**. A daily 100-row watchlist under the same assumptions is
\~$0.34/day (~$10/month).

### Scheduling recipe

1. Create an Apify **Schedule** (e.g. daily at 09:00) pointing at this Actor with your fixed `productIds` input.
2. Add an Actor **integration/webhook** on run success and consume the dataset filtered to `priceChanged = true` (the dataset API supports field filtering), or plug the dataset into Make/Zapier/Google Sheets.
3. The change snapshot lives in the named key-value store `tcgplayer-price-monitor-state` in your account — delete it to reset change detection.

### Data source & respect

Data comes from TCGplayer's public, unauthenticated JSON endpoints (the same
ones the website itself uses) over plain HTTP — no browser, no login, no
CAPTCHA circumvention. The Actor is deliberately polite: low concurrency,
spacing between requests, exponential backoff on 429/5xx, and a descriptive
User-Agent identifying this Actor. It never crawls HTML pages and never touches
seller-filtered pages, which tcgplayer.com's robots.txt disallows. Prices are
public market data; no personal information is collected.

### Limitations

- These are unofficial endpoints; TCGplayer can change them without notice. A schema contract test runs on every run and flags drift immediately; we target same-day fixes, 14 days worst case.
- Search relevance is TCGplayer's — broad queries return whatever the site's search ranks first (may include sealed products; filter with `sealed`/`productLine`).
- `lowestPrice` / `listingCount` come from the search payload and are `null` for products whose metadata lookup failed.
- If more than 30% of requests to an endpoint fail, the run aborts with a clear error rather than emitting partial garbage.

### Support

Use the **Issues** tab on this Actor's page — reports are answered within 24
hours, and breaking upstream changes are fixed within 14 days (usually much
faster).

# Actor input Schema

## `queries` (type: `array`):

Card/product search terms, e.g. "Charizard ex 199/165". Each query is resolved via TCGplayer search.

## `productIds` (type: `array`):

Known TCGplayer product IDs (from product URLs, e.g. 610532 or https://www.tcgplayer.com/product/610532). Skips search; fetched directly. Preferred for scheduled monitoring.

## `productLine` (type: `string`):

Restrict search to one game (search mode only).

## `maxResultsPerQuery` (type: `integer`):

Cap on products resolved per search query (each product can yield multiple printing rows).

## `enableChangeDetection` (type: `boolean`):

Compare against the previous run (named key-value store) and emit priceChanged/prevMarketPrice/delta fields. Rows where the price actually changed are charged additionally as price-change-result events.

## `printings` (type: `array`):

Filter to specific printings, e.g. Normal, Foil, 1st Edition Holofoil (case-insensitive). Empty = all.

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

Off by default - TCGplayer's JSON APIs currently work from datacenter IPs without a proxy. Enable Apify auto proxy only as a fallback if you see blocks.

## Actor input object example

```json
{
  "queries": [
    "charizard ex obsidian flames"
  ],
  "productIds": [],
  "productLine": "",
  "maxResultsPerQuery": 50,
  "enableChangeDetection": true,
  "printings": [],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per product/printing with market price, listed median, change-detection fields, and product metadata.

# 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 = {
    "queries": [
        "charizard ex obsidian flames"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sellavir/tcgplayer-price-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 = { "queries": ["charizard ex obsidian flames"] }

# Run the Actor and wait for it to finish
run = client.actor("sellavir/tcgplayer-price-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 '{
  "queries": [
    "charizard ex obsidian flames"
  ]
}' |
apify call sellavir/tcgplayer-price-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sellavir/tcgplayer-price-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/JFXVotha7mKmtL0bH/builds/JK1ukidFuUoRY0Ivj/openapi.json
