Docs / API reference
Identify a card
Built for card scanners: send what's printed on a card and get the matching cards with their prices.
How it works
Your scanner takes a photo, and an AI vision model (Claude, GPT, Gemini or your own) reads the name and collector number printed on the card. Send those here and you get the best matching cards, each with its current prices, in one request.
Request
https://tcgpriceapi.com/v1/cards/match| Name | Type | Description |
|---|---|---|
namerequired | string | The card's name as printed, such as Charizard. 2 to 150 characters. Not case sensitive. |
number | string | The collector number as printed, such as 4/102, OP01-001 or 025/165. Leading zeros and the "/total" part are optional. |
set | string | The set name, if you know it. Cards from a set whose name contains it come first; others are still returned. |
game | string | Only cards in this game, as listed by /v1/sets. Not case sensitive. |
printing | string | Only this printing, such as Holofoil. |
condition | string | Only this condition, such as Near Mint. |
language | string | Only this language, such as English. |
limit | integer | How many candidates to return, 1 to 10. Defaults to 5. |
Counts as one request and one card, however many candidates it returns. A missing key, a bad body or a used-up limit is turned away before the search runs and is never counted.
curl -X POST "https://tcgpriceapi.com/v1/cards/match" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Charizard", "number": "4/102", "game": "Pokemon", "condition": "Near Mint"}'import requests
response = requests.post(
"https://tcgpriceapi.com/v1/cards/match",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"name": "Charizard",
"number": "4/102",
"game": "Pokemon",
"condition": "Near Mint"
},
timeout=30,
)
response.raise_for_status()
print(response.json())const response = await fetch("https://tcgpriceapi.com/v1/cards/match", {
method: "POST",
headers: {
"X-API-Key": process.env.TCGPRICEAPI_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({"name": "Charizard", "number": "4/102", "game": "Pokemon", "condition": "Near Mint"}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
console.log(await response.json());Response
{
"candidates": [
{
"match": "exact",
"card": {
"card_id": "card_1c3aaea45434413c",
"name": "Charizard",
"set": "Base Set",
"number": "004/102",
"rarity": "Holo Rare",
"game": "Pokemon",
"image_url": "https://tcgplayer-cdn.tcgplayer.com/product/42382_in_1000x1000.jpg",
"median_listing_price": null,
"updated_at": "2026-11-12T01:23:07+00:00",
"skus": [
{
"sku_id": "sku_4687030dac764c67",
"printing": "Holofoil",
"condition": "Near Mint",
"language": "English",
"market_price": 987.13,
"market_price_calculated_at": "2026-11-11T11:47:32.789Z",
"sold_price_low": 695.95,
"sold_price_high": 1500,
"sold_price_count": 14,
"listing_price_low": 785.0,
"listing_price_low_shipping": 0.99,
"listing_price_high": 8999.0,
"listing_price_high_shipping": 0.0,
"listings": 26,
"copies_listed": 28,
"sellers": 21
}
]
}
}
]
}A real card from our database, shortened to one SKU.
candidates come best first. Each has a match label and the card, with the same fields and prices as One card.
exact: same name and number. Safe to use without asking.number: same number, and the name is part of a longer one, such as "Charizard (Shadowless)".name: the name matches but the number doesn't, or you sent no number.
Ask the user to confirm anything that isn't exact. An empty list means nothing matched: try a shorter name.
| Field | Type | Description |
|---|---|---|
candidates | array of MatchCandidate | The best matches first. Empty when nothing matches. |
Candidate
| Field | Type | Description |
|---|---|---|
match | Literal | "exact": same name and number. "number": same number, and the name is part of a longer one. "name": the name matches but the number doesn't, or no number was sent. Ask the user to confirm anything but "exact". |
card | card objects | The card with its current prices, as GET /v1/cards/{card_id} returns it. |