TED Contract Expiry Radar - Recompete Leads
Find EU public contracts approaching expiry from TED award notices: incumbent, buyer, value, end date and renewal options.
datagrit › Data › TCGplayer Price Trends & Sales Velocity Tracker
DataTCGplayer card and sealed product prices with 30 and 90 day price change, sales velocity and days of supply per printing.
TCGplayer Price Trends & Sales Velocity Tracker reads the public TCGplayer catalogue and returns, for every printing and language of a card or sealed product, the current market price, the 30-day and 90-day price change, the units sold in 30 and 90 days, sales per day and days of supply. Results are clean records you can export as JSON, CSV or Excel, call through the Apify API, or connect to n8n, Make and AI agents through MCP.
It is built for card resellers, collectors, investors and price-tracking tools that need to know which cards are moving, not only what they cost today. Choose a game, add search terms, set your filters and run.
Each row is one printing and language of one product, for example the Holofoil English copy of a card. The figures refer to the main condition: Near Mint for cards and Unopened for sealed products.
| productName | setName | variant | language | marketPrice | priceChange30dPct | sold30d | sold90d | soldPerDay | daysOfSupply |
|---|---|---|---|---|---|---|---|---|---|
| Charizard GX - 9/68 (#60 Charizard Stamped) | Battle Academy | Holofoil | English | 14.57 | 8.8 | 35 | 138 | 1.53 | 54.2 |
| Charizard - 3/70 (#39 Charizard Stamped) | Battle Academy | Normal | English | 7.37 | -0.3 | 73 | 132 | 1.47 | 46.3 |
{
"found": true,
"productId": 219059,
"productName": "Charizard GX - 9/68 (#60 Charizard Stamped)",
"productLine": "Pokemon",
"setName": "Battle Academy",
"variant": "Holofoil",
"language": "English",
"condition": "Near Mint",
"sealed": false,
"marketPrice": 14.57,
"priceChange30dPct": 8.8,
"priceChange90dPct": 12.1,
"lowSalePrice90d": 10.2,
"highSalePrice90d": 20,
"sold30d": 35,
"sold90d": 138,
"soldPerDay": 1.53,
"marketPriceLightlyPlayed": 10.67,
"daysOfSupply": 54.2,
"productUrl": "https://www.tcgplayer.com/product/219059"
}
The record also holds set code, card number, rarity, release date, the last sales window, sales across all conditions, listing count, lowest and median listing price, image URL and scrapedAt.
charizard, booster box or lightning bolt. Leave the terms empty and pick a game, a set or both to read a whole game or set; this also works when you call the Actor through the API.The market price is TCGplayer's own market price for the main condition in the latest 3-day window. It is an average of recent sales and carries over when nothing sold, so a price change is reported only when the printing had at least one sale in that window; otherwise the field is empty. Units sold are summed over the 3-day windows of the last 30 or 90 days, for the main condition. The 90-day change needs at least 84 days of history, so new releases have an empty 90-day change. Days of supply divides the active listings of the whole product by its average daily sales across all printings and conditions.
TCGplayer reports sales per 3-day window, so figures are estimates of real sales volume rather than an audited count.
The Actor reads only publicly available catalogue and sales information and does not log in or bypass access controls. You are responsible for using the data in line with applicable laws and the terms of the source. If you find an issue, open it in the Issues tab; problems are answered within one business day.
Every result is one flat record, so it drops straight into a spreadsheet, a database or a CRM.
| Field | Type | Description | Example |
|---|---|---|---|
found | boolean | False only on the single status row emitted when nothing matched; every other field of that row is empty. | true |
productId | integer | TCGplayer product id. One product can produce several rows, one per printing and language. | 219059 |
productName | string | Name of the card or sealed product as listed on TCGplayer. | Charizard GX - 9/68 (#60 Charizard Stamped) |
productLine | string | Trading card game (product line) the product belongs to. | Pokemon |
setName | string | Set or expansion name. | Battle Academy |
setCode | string | Short set code used by TCGplayer, when it has one. | BTA |
cardNumber | string | Collector number printed on the card. Empty for sealed products and for cards without a number. | 009/068 |
rarity | string | Rarity as named by TCGplayer. Empty for some sealed products. | Promo |
releaseDate | string | Release date of the card or product in YYYY-MM-DD form, when TCGplayer has it. | 2020-07-31 |
variant | string | Printing of the product, for example Normal, Foil, Holofoil or Reverse Holofoil. Each printing has its own price and sales. | Holofoil |
language | string | Language of the printing. | English |
condition | string | Condition the price and sales figures refer to: Near Mint for cards, Unopened for sealed products. If a printing has no Near Mint sales series, the condition with the most sales is used. | Near Mint |
sealed | boolean | True when the price series is for an unopened sealed product such as a booster box. | false |
marketPrice | number | TCGplayer market price of the main condition in the most recent 3-day window. It is an average of recent sales, so it carries over when nothing sold. | 14.57 |
priceChange30dPct | number | Change of the market price between the window about 30 days ago and the latest window. Empty when a price is missing or when the printing had no sales in the last 30 days, because the market price then only carries over. | 8.8 |
priceChange90dPct | number | Change of the market price between the oldest window of the 90-day history (about 87 days ago) and the latest window. Empty when a price is missing, when the printing has less than 84 days of history, or when it had no sales in 90 days. | 12.1 |
lowSalePrice90d | number | Lowest single sale price (without shipping) in the 90-day history, taken over windows that had sales. | 10.2 |
highSalePrice90d | number | Highest single sale price (without shipping) in the 90-day history, taken over windows that had sales. | 20 |
sold30d | integer | Units sold at the main condition in the last 30 days, summed from the 3-day windows. | 35 |
sold90d | integer | Units sold at the main condition in the 90-day history, summed from the 3-day windows. | 138 |
soldPerDay | number | Average units sold per day at the main condition over the 90-day history (sold90d divided by the days covered). | 1.53 |
lastSaleWindowStart | string | Start date (YYYY-MM-DD) of the most recent 3-day window in which this printing sold. The sale happened on or up to 2 days after this date. | 2026-09-28 |
allConditionsSold90d | integer | Units sold in the 90-day history across all conditions of this printing and language. | 275 |
marketPriceLightlyPlayed | number | Latest market price of the Lightly Played condition. Empty when this condition has no price series. | 10.67 |
marketPriceModeratelyPlayed | number | Latest market price of the Moderately Played condition. Empty when this condition has no price series. | 9.6 |
marketPriceHeavilyPlayed | number | Latest market price of the Heavily Played condition. Empty when this condition has no price series. | 7.4 |
marketPriceDamaged | number | Latest market price of the Damaged condition. Empty when this condition has no price series. | 5.2 |
listingsCount | integer | Number of active seller listings for the whole product, across all printings, languages and conditions. | 114 |
lowestListingPrice | number | Lowest active listing price for the whole product, across all printings and conditions. Not specific to this printing. | 7.21 |
medianListingPrice | number | Median price of the active listings for the whole product, when TCGplayer reports it. | 13.9 |
daysOfSupply | number | Active listings of the whole product divided by its average daily sales across all printings and conditions. A low number means stock sells out fast; empty when nothing sold in 90 days. | 54.2 |
productUrl | string | TCGplayer page of the product. | https://www.tcgplayer.com/product/219059 |
imageUrl | string | Product image on the TCGplayer image server. | https://product-images.tcgplayer.com/fit-in/437x437/219059.jpg |
scrapedAt | string | ISO 8601 timestamp of extraction. | 2026-09-30T08:00:00.000Z |
{
"found": true,
"productId": 219059,
"productName": "Charizard GX - 9/68 (#60 Charizard Stamped)",
"productLine": "Pokemon",
"setName": "Battle Academy",
"setCode": "BTA",
"cardNumber": "009/068",
"rarity": "Promo",
"releaseDate": "2020-07-31",
"variant": "Holofoil",
"language": "English",
"condition": "Near Mint",
"sealed": false,
"marketPrice": 14.57,
"priceChange30dPct": 8.8,
"priceChange90dPct": 12.1,
"lowSalePrice90d": 10.2,
"highSalePrice90d": 20,
"sold30d": 35,
"sold90d": 138,
"soldPerDay": 1.53,
"lastSaleWindowStart": "2026-09-28",
"allConditionsSold90d": 275,
"marketPriceLightlyPlayed": 10.67,
"marketPriceModeratelyPlayed": 9.6,
"marketPriceHeavilyPlayed": 7.4,
"marketPriceDamaged": 5.2,
"listingsCount": 114,
"lowestListingPrice": 7.21,
"medianListingPrice": 13.9,
"daysOfSupply": 54.2,
"productUrl": "https://www.tcgplayer.com/product/219059",
"imageUrl": "https://product-images.tcgplayer.com/fit-in/437x437/219059.jpg",
"scrapedAt": "2026-09-30T08:00:00.000Z"
}
| Field | Name | Type | What it does |
|---|---|---|---|
searchQueries | Search terms | array | Card or product names to search on TCGplayer, for example charizard or booster box. Every term is searched separately and a product found twice is returned once. Leave empty to read a whole product line or set. |
productLine | Game (product line) | string | Limit the search to one trading card game. Choose All games to search every line, which works best together with a search term or a set. |
setNames | Sets | array | Optional. Keep only products from these sets, for example Base Set or swsh-crown-zenith. Use the set name as shown on TCGplayer or the last part of the set URL. A set that does not exist in the chosen game stops the run with an error that lists similar names. |
rarities | Rarities | array | Optional. Keep only these rarities, for example Secret Rare or Special Illustration Rare. Names follow TCGplayer and differ by game. |
productType | Cards or sealed products | string | Single cards and sealed products (booster boxes, packs, decks) are both in the catalogue. Sealed products are the ones sold as Unopened. |
printings | Printings | array | Optional. Keep only these printings, for example Normal, Foil, Holofoil or Reverse Holofoil. Every printing has its own price and sales history. |
languages | Languages | array | Optional. Keep only these languages, for example English or Japanese. Leave empty for all languages. |
minMarketPrice | Minimum market price (USD) | number | Keep only printings whose Near Mint market price (Unopened for sealed products) is at least this amount. 0 disables the filter. |
maxMarketPrice | Maximum market price (USD) | number | Keep only printings whose market price is at most this amount. 0 disables the filter. |
minSold30d | Minimum units sold in 30 days | integer | Keep only printings that sold at least this many units in the last 30 days at the main condition. Use it to drop cards nobody buys. 0 disables the filter. |
minSold90d | Minimum units sold in 90 days | integer | Keep only printings that sold at least this many units in the last 90 days at the main condition. 0 disables the filter. |
minPriceChange30dPct | Minimum 30-day price change (%) | number | Optional. Keep only printings whose market price rose at least this many percent over the last 30 days. Enter 20 to find risers. Negative values are allowed. Printings without a 30-day comparison are skipped. |
maxPriceChange30dPct | Maximum 30-day price change (%) | number | Optional. Keep only printings whose market price changed at most this many percent over the last 30 days. Enter -15 to find fallers. |
sortBy | Order of products to scan | string | Order in which TCGplayer returns products before they are scanned. Relevance follows TCGplayer popularity. Highest price first is useful with a minimum price. |
maxProductsToScan | Maximum products to scan | integer | How many products to read the sales history of, per run across all search terms. One product gives one row per printing and language, so you often get more rows than products. TCGplayer search returns at most 9950 products per search. |
maxItems | Maximum results | integer | Stop after this many rows in total. Each row is one billed result. |
proxyConfiguration | Proxy configuration | object | Optional proxy. Leave disabled: the TCGplayer endpoints used here are public and need none. |
Run the Actor and get the results in one request. Replace YOUR_APIFY_TOKEN with the token from your Apify account settings.
curl -X POST "https://api.apify.com/v2/acts/datagrit~tcgplayer-price-trend-tracker/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchQueries":["charizard"],"productLine":"pokemon","maxProductsToScan":30,"maxItems":50}'import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datagrit/tcgplayer-price-trend-tracker').call({
"searchQueries": [
"charizard"
],
"productLine": "pokemon",
"maxProductsToScan": 30,
"maxItems": 50
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);Install with npm i apify-client.
from apify_client import ApifyClient
import os
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("datagrit/tcgplayer-price-trend-tracker").call(run_input={
"searchQueries": [
"charizard"
],
"productLine": "pokemon",
"maxProductsToScan": 30,
"maxItems": 50
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(items), items[0] if items else None)Install with pip install apify-client.
Every run reads TCGplayer live. The market price is the value of the latest 3-day window of sales, so it can differ by a few cents from the price shown on the product page right now (for example 14.57 against 14.63 for the same foil card). Use it for trends and comparisons between runs, not as a live quote.
One run reads up to 9950 products per search term, set by **Maximum products to scan**. Each product needs one extra request for its sales history, and the Actor keeps a short pause between batches, so expect roughly 6 seconds per 10 products and about a minute for the default 100 products. Rows are limited by **Maximum results**.
Each printing and language is its own row; a card with Normal and Foil copies in two languages gives up to four rows.
The printing had no sales in the window, or it is newer than 84 days for the 90-day change.
Yes, use Apify schedules or call the Actor from your own workflow and compare runs over time.
Open an issue with the input you used; changes at the source are fixed quickly.
Find EU public contracts approaching expiry from TED award notices: incumbent, buyer, value, end date and renewal options.
French company lead lists from Sirene screened by net result and revenue, with net margin, size, matching establishment and optional directors.
Ashby job postings with normalized annual salary ranges, equity flags and new-since-last-run detection.
Open jobs from Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Recruitee and Personio in one schema with normalized salaries.