The Pokémon Company · 41,200 printings
Pokémon card scanner API
The same artwork ships as three different products, and only one of them is worth anything.
- 41,200
- Matchable printings
- 12
- Languages
- 38 ms
- Median match
- $0
- Charged for a miss
The problem
Why Pokémon is hard to identify
Pokémon is the hardest common case in the hobby, because the thing that changes the price is not the picture. A Scarlet & Violet rare exists as a plain holo, a reverse holo with the pattern on the card body instead of the art box, and in some sets a non-holo — same illustration, same number, same name, three different products with three different prices. A scanner that reads the art and stops has a two-in-three chance of being wrong.
On top of that, Japanese sets frequently carry artwork that later appears in a differently numbered English set, promos reuse art with a stamp in the corner, and the set symbol that would settle it is a few millimetres wide and the first thing a thumb covers. The illustration-rare and special-illustration-rare tiers introduced from 2022 onwards make it worse: the extended art crops the frame that normally tells you which tier you are looking at.
So the match here is decided on the collector number and the finish, read together, with the set symbol as a tiebreak rather than the primary key. The response tells you which of the three you are holding, and the price attached is that printing's price rather than the card's average.
wss://api.tcggraph.com/v1/scanidle- 0
- Frames
- 0
- Matched
- 0
- Unbilled
A replayed transcript of Pokémon cards from the public demo catalog, including a frame that does not resolve and is not billed. Nothing is being inferred in your browser.
What settles it
The fields a match is decided on
Read in the order a person grading Pokémon would read them. The match returns all of them, so you never have to infer a printing from a card name.
| Field | Why it decides the price |
|---|---|
| Collector number | The 6/165 in the corner pins the set position even when the symbol is obscured. |
| Finish | Holo, reverse holo and non-holo are separate products at separate prices. |
| Set symbol | Disambiguates reprints that share both art and number across sets. |
| Language | Japanese and English printings of the same art are different SKUs entirely. |
| Regulation mark | The letter in the bottom corner dates the print run and settles Standard legality. |
Where scanners fail
Pokémon traps that return the wrong printing
Each of these is a case where the artwork is right and the answer is still wrong. A confident wrong match costs more than a refused one, which is why an unresolved frame is free.
Reverse holos read as holos
The illustration is identical and the pattern sits outside the art box, so a crop that centres the artwork loses the only visible difference. Reverse holos usually trade below the holo and sometimes above it, so the error runs both ways.
Promo stamps
Prerelease, Staff and league promos reuse an existing card's art with a stamp added. The stamp is the entire difference and it is the part of the card most likely to be under a sleeve seam.
WOTC-era editions
Base Set cards are separated by a 1st Edition stamp and by whether a drop shadow sits beside the art box. The gap between shadowless and unlimited is several hundred dollars on a Charizard.
Integrate it
Scanning Pokémon in about fifteen lines
Pin the socket to one game and matching gets both faster and more certain, because there are fewer near-identical candidates to rule out.
The card inside a match is the same object /v1/cards?game=pokemon returns, including the Pokémon-specific fields. If you already read this API, a scanner is a new input to code you have written rather than a second integration.
Scanning is metered separately from data, so pointing a camera at a stack all morning cannot spend the credits your repricer needs at midnight.
Scan pricing
$29/mo · 6,000 matched cards
Or $0.012 per matched card with no plan at all. Down to $0.0025 at volume, and a frame we cannot place is never billed.
Compare scan plansconst socket = new WebSocket("wss://api.tcggraph.com/v1/scan", [
"tcggraph.v1",
`bearer.${process.env.TCGGRAPH_KEY}`,
]);
socket.onopen = () =>
socket.send(
JSON.stringify({
type: "config",
games: ["pokemon"],
minConfidence: 0.92,
prices: ["cardmarket", "tcgplayer"],
}),
);
socket.onmessage = (event) => {
const frame = JSON.parse(event.data);
for (const match of frame.matches ?? []) {
// The printing, not just the card: set, number, language, finish.
intake.add(match.printing, match.card.prices);
}
// Unresolved frames arrive too, and cost nothing.
for (const miss of frame.unresolved ?? []) showHint(miss.reason);
};
for await (const jpeg of camera.frames()) socket.send(jpeg);{
"requestId": "scan_01K5Z8P4XQJ7YB3M",
"latencyMs": 41,
"matches": [
{
"confidence": 0.9962,
"billed": true,
"box": {
"x": 0.171,
"y": 0.064,
"w": 0.658,
"h": 0.872
},
"printing": {
"id": "pkm_sv3pt5_199",
"language": "en",
"finish": "holofoil",
"edition": "unlimited",
"collectorNumber": "199/165"
},
"card": {
"id": "pkm_sv3pt5_199",
"game": "pokemon",
"name": "Charizard ex",
"set": {
"code": "sv3pt5",
"name": "151"
},
"rarity": "Special Illustration Rare",
"prices": [
{
"source": "tcgplayer",
"region": "NA",
"currency": "USD",
"market": 370.48
},
{
"source": "cardmarket",
"region": "EU",
"currency": "EUR",
"market": 312.67,
"trend": 312.67,
"sellers": 86
}
]
},
"alternatives": []
}
],
"scans": {
"billed": 1,
"remaining": 5842
}
}FAQ
Scanning Pokémon
Start with Pokémon, add the rest for nothing
One key covers every game we carry, and scans are pooled across all of them. There is no per-game surcharge and no separate model to enable — a stack with Pokémon, Magic and One Piece mixed together resolves card by card without you sorting it first.