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-Key header 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 minute
  • X-Requests-Remaining-Today and X-Requests-Remaining-Month
  • GET /v1/usage shows 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.