Wizards of the Coast · 108,500 printings
Magic card scanner API
Thirty years of reprints means one card name maps to eighty different products.
- 108,500
- Matchable printings
- 11
- Languages
- 38 ms
- Median match
- $0
- Charged for a miss
The problem
Why Magic is hard to identify
Magic's problem is scale in two directions at once. A staple like Lightning Bolt has been printed in dozens of sets, most with different artwork, and Secret Lair and Universes Beyond releases have added treatments that look nothing like the original. At the same time the reverse is true: the same artwork is reprinted across sets with only a set symbol and a copyright line to tell them apart, and some promos differ from the main-set printing by nothing but a stamp.
The modern frame treatments multiply it again. Borderless, extended art, showcase, retro frame and textless versions of one card in one set are separate products with separate prices, and foil and etched foil are separate again. A scanner keyed on artwork will confidently return the wrong one of five printings from the same booster.
The field that actually settles it is the collector number in the bottom-left, printed on everything since 2018 alongside the set code and the language letter. Read together with the frame treatment and the foiling, that is a unique product. Before 2018 the copyright line and the artist credit do the same job, which is why older cards resolve on a different key.
wss://api.tcggraph.com/v1/scanidle- 0
- Frames
- 0
- Matched
- 0
- Unbilled
A replayed transcript of Magic 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 Magic 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 and set code | Since 2018 the bottom-left line is a unique product identifier. |
| Frame treatment | Borderless, extended, showcase and retro are separate printings at separate prices. |
| Foiling | Non-foil, foil and etched foil diverge by multiples on the same card. |
| Copyright line | The primary discriminator for pre-2018 printings with no collector number. |
| Language | Eleven printed languages, and Japanese alternate-art printings carry their own premium. |
Where scanners fail
Magic 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.
Reprints that share art
Core-set reprints often reuse the illustration exactly. The set symbol and the copyright year are the only visible difference, and the price gap between a 1997 and a 2021 printing of the same card can be two orders of magnitude.
Five printings, one booster
A mythic from a recent set can exist as regular, foil, extended art, borderless and showcase, all pulled from the same box. Matching on the illustration alone cannot distinguish four of the five.
Old-border edge cases
Alpha is separated from Beta by corner radius and from Unlimited by border colour. These are the most valuable cards in the game and the distinction is geometric rather than textual.
Integrate it
Scanning Magic 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=magic-the-gathering returns, including the Magic-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: ["magic-the-gathering"],
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": "mtg_bd8fa327-dd4",
"language": "en",
"finish": "holofoil",
"edition": "unlimited",
"collectorNumber": "4"
},
"card": {
"id": "mtg_bd8fa327-dd4",
"game": "magic-the-gathering",
"name": "Black Lotus",
"set": {
"code": "VMA",
"name": "Vintage Masters"
},
"rarity": "Bonus",
"prices": []
},
"alternatives": []
}
],
"scans": {
"billed": 1,
"remaining": 5842
}
}FAQ
Scanning Magic
Start with Magic, 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 Magic, Pokémon and One Piece mixed together resolves card by card without you sorting it first.