This guide gets you from nothing to live card prices in your own code. It uses the same four calls almost every card app is built on: find a card, read its prices, price many cards at once, and read its sold history.
You need an API key. Sign in at tcgpriceapi.com/dashboard with your email and the key is waiting there. The free plan needs no credit card.
The basics
- Base address:
https://tcgpriceapi.com - Send your key in the
X-API-Keyheader on every request. - Answers are JSON. Prices are in US dollars and arrive exactly as reported. A price we don't have comes back as
null, never as a guess.
Keep the key out of your code. Both examples below read it from an environment variable called TCGPRICEAPI_KEY.
Step 1: find the card
Search by name, and narrow it down with the game, the set or the collector number. Search results don't include prices, which keeps them fast.
GET /v1/cards?game=Pokemon&name=Charizard&limit=5
Python, with the requests library:
import os
import requests
API = "https://tcgpriceapi.com"
HEADERS = {"X-API-Key": os.environ["TCGPRICEAPI_KEY"]}
found = requests.get(f"{API}/v1/cards", headers=HEADERS, timeout=30,
params={"game": "Pokemon", "name": "Charizard", "limit": 5})
found.raise_for_status()
for card in found.json():
print(card["card_id"], card["name"], "|", card["set"], card["number"])
JavaScript (Node 18 or newer, or any modern browser backend):
const API = "https://tcgpriceapi.com";
const headers = { "X-API-Key": process.env.TCGPRICEAPI_KEY };
const params = new URLSearchParams({ game: "Pokemon", name: "Charizard", limit: "5" });
const response = await fetch(`${API}/v1/cards?${params}`, { headers });
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
for (const card of await response.json()) {
console.log(card.card_id, card.name, "|", card.set, card.number);
}
Each result has a card_id. It never changes, so store it in your own database and you won't need to search for that card again. The game names are listed by GET /v1/sets: for example Pokemon, Pokemon Japan, Magic: The Gathering and One Piece Card Game.
Step 2: read one card's prices
GET /v1/cards/{card_id}?condition=Near Mint
A card comes back with a list of SKUs. A SKU is one printing, condition and language, such as Holofoil, Near Mint, English, and every price belongs to one SKU. Leave out condition to get every condition.
card_id = found.json()[0]["card_id"]
answer = requests.get(f"{API}/v1/cards/{card_id}", headers=HEADERS, timeout=30,
params={"condition": "Near Mint"}).json()
card = answer["cards"][0]
for sku in card["skus"]:
print(sku["printing"], sku["condition"], "market", sku["market_price"],
"cheapest listing", sku["listing_price_low"], "+", sku["listing_price_low_shipping"], "shipping")
The fields you'll use most:
| Field | What it is |
|---|---|
market_price |
The current value of that SKU, based on recent sales |
sold_price_low, sold_price_high |
The lowest and highest of those recent sales |
listing_price_low |
The cheapest copy for sale right now, with listing_price_low_shipping |
copies_listed, sellers |
How many copies are for sale and how many sellers have one |
image_url |
A picture of the card, on the card itself |
Step 3: price many cards in one request
When you already have a list of card_id values, such as a collection, a deck or a store's stock, send them together:
POST /v1/cards/lookup
{"card_ids": ["card_...", "card_..."], "condition": "Near Mint"}
const lookup = await fetch(`${API}/v1/cards/lookup`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ card_ids: cardIds, condition: "Near Mint" }),
});
const { cards, not_found } = await lookup.json();
A lookup counts as one request however many cards it holds. How many cards fit in one request depends on the plan: 1 on Free, 20 on Starter, 100 on Pro, 200 on Business and 250 on Enterprise. A card_id that doesn't exist is listed under not_found instead of failing the whole request.
Step 4: read sold history
GET /v1/cards/{card_id}/history?timeframe=30d&condition=Near Mint
The timeframe can be 7d, 30d, 3m, 6m or 1y. You get daily rows for 7 and 30 days, a row every 3 days for 3 months, and weekly rows for 6 months and a year. Each row has the market price, the lowest and highest sale (with and without shipping) and how many copies sold.
history = requests.get(f"{API}/v1/cards/{card_id}/history", headers=HEADERS, timeout=30,
params={"timeframe": "30d", "condition": "Near Mint"}).json()
for sku in history["cards"][0]["skus"]:
for row in sku["history"]:
print(row["date"], row["market_price"], row["quantity_sold"])
Free includes 7 days of history, Starter 30 days, and Pro and up a full year. History for 7 or 30 days counts as one request. History for 3 months or more counts one request for each card.
Handling limits
Every answer tells you how much you have left, so your code can slow down before anything is refused:
RateLimit-Remaining: requests left this minuteX-Requests-Remaining-TodayandX-Requests-Remaining-MonthGET /v1/usageshows your plan and what you've used, and doesn't count as a request
If you do reach a limit, the API answers 429 with a Retry-After header saying how many seconds to wait. Requests turned away at a limit aren't counted.
import time
def get(path, **params):
while True:
response = requests.get(f"{API}{path}", headers=HEADERS, params=params, timeout=30)
if response.status_code != 429:
response.raise_for_status()
return response.json()
time.sleep(int(response.headers.get("Retry-After", "60")))
Tips that save requests
- Search once, then store the
card_id. Most apps never search for the same card twice. - Market prices and recent sales refresh every 6 hours, and live listings and sold history every day or two. Caching an answer for an hour or more costs you nothing in freshness.
- Use the bulk lookup whenever you have more than one card. Ten cards in one request use one request, not ten.
Next steps
- The docs cover every endpoint, field and error.
- Building something specific? The other guides walk through card scanners, collection trackers, store repricing and Discord bots.
- Using an AI coding assistant? Connect it to the MCP server and it can look prices up while it writes your code.