Skip to content
Prepaid, no subscription trap. Top up a balance and we draw the monthly fee from it.
tcggraph

Collection tracking

A collection tracker is a pricing problem wearing a catalogue costume

Build a collection tracker across nine games with per-printing identity, both markets priced, and a valuation your users can actually act on.

The problem

Where this project usually goes wrong

The catalogue half of a collection tracker looks like the hard part and is not. Users add cards, you store ids, you render images. The part that decides whether people keep using it is the total at the top of the screen.

That total is where most trackers quietly fall apart. A user owns a specific printing — this set, this collector number, this finish — and if your data model collapses those into one card record, you show the same value for a bulk reverse holo as for the version that is worth two hundred times more. Users spot it immediately and stop trusting every other number you display.

The second failure is currency. A European user shown a dollar figure converted at today's rate is being told a number that does not exist in their market, because European and American prices diverge for reasons that have nothing to do with the exchange rate.

What it needs

The fields that decide it

Per-printing identity
Every finish, language and promo treatment is its own row with its own price, so a user's specific copy is the thing being valued.
prices[] by source
European and American quotes on the same object, each in its native currency, so you show the user the market they are actually in.
listType: buylist
What the collection would fetch if sold, which is the honest companion to what it would cost to rebuild.
updatedAt on every quote
Lets you show how fresh a valuation is rather than implying every number is live.
100 cards a page, 2 credits
A page carries every card's prices, so a local copy of the games you track is a few hundred credits to build and the same to refresh.

In practice

The two queries that matter most

Written for apps where people catalogue what they own. Both work the same way for every game we carry.

Value a shelf of cards in one round trip

Value a shelf of cards in one round trip
{
  a: card(id: "pkm_sv3pt5_6") { ...Valued }
  b: card(id: "op_op05_119") { ...Valued }
  c: card(id: "lor_9_242") { ...Valued }
}

fragment Valued on Card {
  id
  name
  set { name code }
  collectorNumber
  prices {
    source
    listType
    currency
    market
    updatedAt
  }
}

Aliases put the whole shelf in one request, with both markets and both sides of each. Looping one HTTP call per card is the single most common reason a tracker feels slow.

Build the local catalogue, then refresh it nightly

Build the local catalogue, then refresh it nightly
# Page a game at 100 cards a time; every card arrives with its prices.
# 20,000 printings is 200 pages and 400 credits, at setup and again each night.
page=1
while :; do
  body=$(curl -sG "https://api.tcggraph.com/v1/cards" \
    -d game=pokemon -d limit=100 -d page=$page \
    -H "Authorization: Bearer $TCGGRAPH_KEY")
  echo "$body" | jq -c '.data[]' >> pokemon.ndjson
  [ "$(echo "$body" | jq '.meta.hasMore')" = "true" ] || break
  page=$((page + 1))
done

There is no bulk export or changed-since filter; the page is the unit. Between passes, set.published and card.created webhooks say when something new exists.

Avoid these

Three mistakes that are expensive to undo

Each of these is cheap to get right at the start and painful to retrofit once you have user data shaped the wrong way.

  • Storing a card name instead of a printing id

    Names are not unique across sets, reprints or languages, and a user who owns the expensive printing will not accept being shown the cheap one's price. Store the printing id; render the name.

  • Valuing everything at retail

    A collection marked at retail is a number the owner can never realise. Show both, or at minimum label clearly which one you are showing.

  • Converting one market's price into another currency

    An FX conversion of an American price is not a European price. Ask for the market your user is in and show it natively.

Questions

Collection tracker, answered

Ship your card app this weekend

$29 a month for 10,000 credits, no sales call and no contract. Pick a plan and your first query runs in under a minute.