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

Marketplaces

Every card marketplace needs the same four things

Catalog, prices, images and a scanner, from one API: per-printing pricing in both currencies, the cheapest copy per language and condition, and a listing flow that starts with a photo.

The problem

Where this project usually goes wrong

A marketplace is four data problems wearing one product. A catalog so a seller lists the right printing and a buyer finds it; a reference price so neither side is guessing; images so a listing looks like a listing; and a way to turn a photo into a printing so sellers do not give up at the search box. Teams usually stitch these from three or four providers with four identifier schemes, and the joins between them are where the bugs live.

The pricing trap is pricing European stock off American data. The two markets diverge for real reasons — different print runs, different distribution, different demand — and localised printings often have no American price at all. Applying an exchange rate to a dollar figure produces a euro number that no European buyer or seller recognises.

The listing trap is the language. A German seller lists a German copy; a buyer comparing against the English price sees a bargain or a rip-off that is neither. The reference has to be the cheapest German copy for sale, in the condition on offer, or the marketplace is arbitraging its own users.

What it needs

The fields that decide it

id, set, collectorNumber, language
Every printing of every game under one id scheme, so a listing points at exactly one product and the price, image and scan result all agree on which.
Native prices per market
Price the market your buyer is actually in, in the currency it trades in, with no conversion step to be wrong about.
languagePrices[] with conditions[]
The cheapest Cardmarket copy per language and per condition, so a Near Mint German listing is checked against Near Mint German copies rather than the product-wide low.
trend, avg7 and sales
A trend price is far more stable to reprice against than a spot low one listing can move, and sales (GET /v1/prices/{id}/sales) is what buyers actually paid, by day.
images.small / normal / large
Three renditions on a CDN, so a listing grid, a detail page and a zoom each load the size they need and nothing else.
POST /v1/scan
A photo in, a printing out, over one REST call or a socket for live camera. The seller confirms rather than searches, and the listing carries the right id from the first second.

In practice

The two queries that matter most

Written for anyone listing cards for sale online. Both work the same way for every game we carry.

Create a listing from a photo

Create a listing from a photo
curl -X POST "https://api.tcggraph.com/v1/scan" \
  -H "Authorization: Bearer $TCGGRAPH_KEY" \
  -F image=@seller-photo.jpg \
  -F games=pokemon \
  -F minConfidence=0.92

The match carries the full card record with prices and images attached, so the listing form is pre-filled from one response. Below the threshold the frame comes back unresolved and unbilled, with the reason, so you can tell the seller what to fix.

The reference price for a German Near Mint copy

The reference price for a German Near Mint copy
{
  card(id: "pkm_sv3pt5_6") {
    name
    prices { source currency low trend avg7 }
    languagePrices {
      language
      low
      lowNm
      offers
      truncatedAbove
      conditions { condition low offers }
    }
  }
}

Cardmarket's own low is the cheapest copy in any language. languagePrices splits it: the German row's lowNm is the number a German Near Mint listing should be checked against. Per-language rows are live for Pokémon, One Piece, Lorcana, Dragon Ball Super and Yu-Gi-Oh! and being read for Star Wars: Unlimited, scheduled from every 6 hours to weekly by how actively the card trades; a row can run behind its schedule while the crawl catches up, and each row says when it was actually read.

Reprice a shelf against the European trend

Reprice a shelf against the European trend
curl -G "https://api.tcggraph.com/v1/cards" \
  -d game=pokemon \
  -d source=cardmarket \
  -d minPrice=5 \
  -d sort=-price \
  -H "Authorization: Bearer $TCGGRAPH_KEY"

Filtering and sorting happen on the market you name, so the bounds mean euros when you ask for the European market and dollars when you ask for the American one.

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.

  • Checking a localised listing against the product-wide low

    Cardmarket's low is the cheapest copy in any language, usually not the one being listed. Use the matching languagePrices row, and its conditions[] entry for the condition on offer, or the check is against the wrong product.

  • Repricing off the lowest listing

    The low is the most volatile number in the response and often reflects one seller clearing stock. Trend and rolling averages are what a stable repricer should track.

  • Blending foil and nonfoil

    They are separate products with separate demand curves. A blended figure is simultaneously too high for one and too low for the other.

  • Rebuilding your catalogue nightly

    Card data changes rarely and prices change daily. Page the catalog once, subscribe to set.published and card.created, and refresh prices on their own schedule.

Questions

Marketplace and storefront, 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.