Skip to content
Prepaid, no subscription trap. Top up a balance and we draw the monthly fee from it.
tcggraph

REST reference

Plain JSON over HTTPS, one filter grammar for every game. Every endpoint has its own page with every parameter, default, response field and error, and real responses from the live API.

Base URL

https://api.tcggraph.com/v1

HTTPS only. Every response body is JSON, errors included; the exceptions are a 304, which has no body, and a 405 for a method a path does not take, which has an empty one. The machine-readable description of everything on these pages is the OpenAPI 3.1 document at https://api.tcggraph.com/openapi.json, generated from the same source as these pages; Clients shows how to generate a typed client from it.

curl "https://api.tcggraph.com/v1/cards?game=pokemon&name=charizard&limit=1" \
  -H "Authorization: Bearer $TCGGRAPH_API_KEY"

Authentication

Send your key as Authorization: Bearer tcg_live_..., or as ?api_key= where a header cannot be set; the header wins when both are sent. A missing or unknown key is a 401. Details, including origin restrictions for keys used in a browser, are on Authentication.

Versioning

There is one version, v1, in the path. There is no version header and no per-key pinning: every key gets the API as it is today. New fields, endpoints and enum values are added to v1, so ignore fields you do not know rather than failing on them. What changed and when is in the changelog.

Conventions

// 2xx
{ "data": /* an object or an array */, "meta": { /* paging, echoes, provenance */ } }

// 4xx and 5xx
{ "error": { "code": "invalid_request", "message": "...", "details": { /* field, allowed, ... */ } } }
  • Amounts are numbers in major units of the stated currency: 8.44 is €8.44. Nothing is converted between currencies.
  • Timestamps are ISO 8601 in UTC (2026-09-23T04:12:09.000Z); dates are YYYY-MM-DD.
  • Null and empty: a field is null only where its type says so, and a list with nothing in it is []. Every documented field is present unless its row says it is absent unless it applies.
  • Ids are stable strings (pkm_sv3pt5_6). If a set is re-keyed, the old id keeps answering on every by-id endpoint with meta.movedFrom.
  • Nothing is silently ignored: an unknown filter or parameter, or a value outside a parameter’s list, is a 400 naming it with the values that would have worked. Endpoints that take no query parameters (the games, a card by id, the webhook and watch lists) ignore them.
  • Caching: responses carry an ETag. Send it back as If-None-Match and an unchanged resource is a 304 with no body, costing 0 credits. A 304 carries only ETag and Cache-Control; the credit headers are on your next 200.
  • CORS is open to every origin, so the API can be called from a browser; restrict the key to your origins when you do.

Endpoints

Games

EndpointWhat it doesCredits
GET/v1/gamesEvery supported game, with what is indexed for it right now.1
GET/v1/games/{slug}One game, with its gameData schema: the fields every card of the game carries and you can filter on.1

Sets

EndpointWhat it doesCredits
GET/v1/setsSets across every game, newest first.1
GET/v1/sets/{code}One set and a page of its cards.2

Cards

EndpointWhat it doesCredits
GET/v1/cardsSearch and filter the catalogue. Every filter is optional and they combine with AND.2
GET/v1/cards/{id}One printing by id, in any language.1

Prices

EndpointWhat it doesCredits
GET/v1/prices/{cardId}Every current quote for one card: marketplace, graded and per-language, in one call.1
GET/v1/prices/{cardId}/historyOne quote over time: a marketplace series, or a graded slot.3
GET/v1/prices/{cardId}/salesWhat copies actually sold for on Cardmarket, day by day.3

Webhooks

EndpointWhat it doesCredits
POST/v1/webhooksRegister an HTTPS endpoint for signed event deliveries.0
GET/v1/webhooksEvery endpoint registered on the account.0
GET/v1/webhooks/{id}One endpoint and its last 20 deliveries.0
DELETE/v1/webhooks/{id}Stop deliveries and remove the endpoint.0

Price watches

EndpointWhat it doesCredits
POST/v1/watchesFire price.threshold.crossed when one quote of one card crosses a price.0
GET/v1/watchesEvery watch on the account, and how many the plan allows.0
DELETE/v1/watches/{id}Stop watching.0

Card recognition (POST /v1/scan and the camera socket) is metered separately and documented on Card recognition. GraphQL is one request for 2 credits, on GraphQL.

Objects

Filtering

/v1/cards and /v1/sets/{code} take the same filters as query parameters, combined with AND. The named ones (name, set, rarity, …) mean the same thing in every game; anything else is a field of the game’s gameData, which each game declares and GET /v1/games/{slug} returns as schema. A name that is neither is a 400 with a suggestion where one is obvious (q suggests name). How each filter matches, exactly, is on List cards.

These are run against the live API every time the reference is rebuilt; the count is what each returned.

RequestWhat it showsMatches
/cards?game=pokemon&name=charizardPart of the English name, any case.158
/cards?game=pokemon&text=discardPart of the rules text.15
/cards?game=pokemon&rarity=double rareThe whole rarity, any case.514
/cards?game=pokemon&set=151&collectorNumber=006/165One printing: set by name, number as printed.1
/cards?game=one-piece&collectorNumber=OP05-119A printed number shared by parallels: the base card and every parallel of it.11
/cards?game=pokemon&artist=mitsuhiro aritaThe whole artist credit.723
/cards?game=pokemon&source=cardmarket&minPrice=50&maxPrice=500€50 to €500 on Cardmarket.1,141
/cards?game=pokemon&region=NA&sort=-priceDearest first, in USD on TCGplayer.26,466
/cards?game=pokemon&language=ja&name=pikachuJapanese printings, searched by English name.397
/cards?game=pokemon&language=fr&name=dracaufeuFrench printings, searched by the name printed on them.103
/cards?game=one-piece&language=ja&name=ルフィJapanese printings by their Japanese name.154
/cards?game=pokemon&types=fire&minHp=300A list field and a numeric bound from gameData.71
/cards?game=yugioh&attribute=light&atk=3000Scalar gameData fields, any case.324
/cards?game=yugioh&banlist.tcg=forbiddenA dotted path into an object field.475
/cards?game=disney-lorcana&ink=amethyst&inkable=falseBooleans are matched as text.135
/cards?game=pokemon&sort=-gradedPrice&grader=psa&grade=10Dearest PSA 10 first.26,466

Price filters read one marketplace, cardmarket unless source or region says otherwise, and are in its currency: minPrice=50 is €50 on Cardmarket and $50 on TCGplayer. Every card still returns all of its quotes; meta.priceSource says which one the filter read. Prices covers the markets.

Set codes are unique within a game and language, not across the catalogue: OP16 is a One Piece set and a Yu-Gi-Oh! set, and Pokémon sv3 is a different set in English and Japanese. Name the game when you name a set.

Pagination

Lists take page (from 1) and limit (1 to 100, default 20) and return meta.page, meta.limit, meta.totalCount, meta.totalPages and meta.hasMore. Loop until hasMore is false. Order is stable across pages: every card sort breaks ties on the id, and every set sort on the code. Each page is billed as one call, so limit=100 is the cheapest way through a long list.

async function* allCards(params) {
  for (let page = 1; ; page++) {
    const query = new URLSearchParams({ ...params, page: String(page), limit: "100" });
    const response = await fetch(`https://api.tcggraph.com/v1/cards?${query}`, {
      headers: { Authorization: `Bearer ${process.env.TCGGRAPH_API_KEY}` },
    });
    const body = await response.json();
    if (!response.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
    yield* body.data;
    if (!body.meta.hasMore) return;
  }
}

for await (const card of allCards({ game: "pokemon", set: "sv3pt5" })) console.log(card.id);

Credits and limits

Each endpoint’s cost is in the table above and on its page, and every 2xx response says what it cost in X-Credits-Cost. A 4xx or a 304 costs nothing and carries no credit headers. Each key is also limited per minute, and each account per day; both answer 429 with Retry-After. Rate limits & credits has the numbers per plan and every header.

Game-specific fields

Each game’s page lists its gameData fields, their types and a real value: