🔵 IN THIS GUIDE: How to pull Magic: The Gathering card data and both marketplace prices (TCGplayer in USD, CardMarket in EUR) from one REST API. The catalog is free, and whole-set prices and analytics start at Ultra. Every example below comes from a real API response.

Quick Answer: The PokéWallet API returns TCGplayer (USD) and CardMarket (EUR) prices for a Magic card in a single response, under /mtg/, with the same key you use for the other 11 games. The catalog (514 sets, cards, search, images) is free at 100 requests/hour. Prices for whole sets and the analytics endpoints need Ultra (€29/month) or the 7-day trial; Pro is not enough. Prices refresh once a day.

Magic has the biggest card catalog of any game this API covers, and its prices live on two marketplaces. US sellers watch TCGplayer. European buyers watch CardMarket. If you build a price tracker, a collection app or a Discord bot, you need both, and you don't want to maintain two integrations to get them.

This guide covers what the mtg endpoints return, how to make a first request, what stays free, what needs Ultra, and how to call it from JavaScript and Python. It also covers where Scryfall is the simpler choice. It is the third game guide in the series, after Riftbound and Disney Lorcana.

The angle isn't "better than the alternatives". It's one key and one response shape across twelve games, with two marketplaces side by side. The multi-game overview covers that bigger picture. The full reference is in the Magic: The Gathering API documentation.

Key Takeaways

  • 🆓 The catalog is free on every plan. /mtg/sets, /mtg/sets/:set, /mtg/cards/:id, /mtg/search and /mtg/images/:id cost nothing beyond the free tier's 100 requests/hour and 1,000/day.
  • 💰 A single card or search call already includes prices. Both return TCGplayer and CardMarket prices on every plan. The set card list (/mtg/sets/:set) does not.
  • 🔐 Whole-set prices and analytics need Ultra. /mtg/prices/:set, statistics, completion value, trending sets and top cards require Ultra (€29/month) or the 7-day trial. Pro does not unlock them for Magic.
  • 🌍 Two currencies, never converted. TCGplayer is USD, CardMarket is EUR, and the completion-value response says cross-currency comparison isn't available.
  • 🎴 CardMarket prices come per finish. Each card carries separate normal and foil entries, and updated_at marks the last price change.
  • 🧹 Clean the card text. card_text contains HTML <br> tags and Windows line endings, so strip them before rendering.

What the API Covers

Magic sits under the game code mtg. The public GET /games endpoint lists it with its routes:

RouteWhat it doesPlan
/mtg/setsList every setFree
/mtg/sets/:setOne set and its cardsFree
/mtg/cards/:idOne card with TCGplayer and CardMarket pricesFree
/mtg/search?q=Search cards, prices includedFree
/mtg/images/:idCard imageFree
/mtg/prices/:setPrices for a whole setUltra
/mtg/sets/:set/statisticsPrice statistics and rarity breakdownUltra
/mtg/sets/:set/completion-valueCost to complete a setUltra
/mtg/sets/trendingSets moving over 7 daysUltra
/mtg/analytics/top-cardsTop cards by price or growthUltra

The /games response files the last five under pro_endpoints. For the ten games beyond Pokémon and One Piece, those need Ultra or the 7-day trial, and Pro does not cover them.

The set list reports 514 sets. Each entry has a set_code, a set_id, a card_count, a sealed_count, a language and a release date. Two real entries show the range:

  • 10th Edition (10E, set_id 1): 389 cards, 9 sealed products, released 13 July 2007.
  • 2021 Lunar New Year (pl21, set_id -42): 1 card, no sealed products, release date null.

So promo and oddball sets are in the list, and a release date can be missing. Don't treat release_date as always present.

Cards carry Magic-specific fields on top of the common ones: sub_type (for example "Legendary Creature — Eldrazi"), flavor_text, power (p), toughness (t) and upc. In the samples card_type came back null, so read the type line from sub_type.

Your First Request

Every request goes to https://api.pokewallet.io with your key in the X-API-Key header. Start with a search:

curl -H "X-API-Key: $POKEWALLET_API_KEY" \
  "https://api.pokewallet.io/mtg/search?q=Emrakul"

The query returned 53 matches in total. The metadata block tells you where the prices came from:

Metadata fieldValue
total_count53
tcg (cards with TCGplayer data)51
cardmarket (cards with CardMarket data)47
both_sources45
tcg_only6
cardmarket_only2

Not every card exists on both marketplaces, so handle a missing side. In the first search result, Emrakul, the Aeons Torn (Pro Tour Promos, foil only), both sources were present:

SourceFinishFigureValue
TCGplayer (USD)Foilmarket price$123.86
TCGplayer (USD)Foillow / high$104.13 / $231.50
CardMarket (EUR)Foiltrend€47.18
CardMarket (EUR)Foilavg€48.99

Prices as returned by the API for the Pro Tour Promos foil, stamped 30 September 2026.

CardMarket also returned a normal entry for that card, with only a low of €36.05 and every other field null. A null means no data, not zero. Check for it before you do arithmetic.

Free Endpoints

Sets and cards

GET /mtg/sets/24684 returned Reality Fracture (FRA), released 2 October 2026. It accepts either the set_id or the set_code. If a set_code is ambiguous, the API answers with candidates, so prefer the numeric set_id in code you ship.

The card list is paginated, and the pagination object reports total and total_pages. The sample was fetched with a page size of one, which is why it shows 782 pages. The first card is Emrakul, the Exigent Doom, mythic, in Normal and Foil.

In that card list the price arrays were empty:

"tcgplayer": { "prices": [], "url": "https://www.tcgplayer.com/product/716715/..." }

That is by design: the set card list gives you the card and the marketplace link, not the prices. To get prices, call the single-card endpoint or search, or use the Ultra set-price endpoint below.

One card, both marketplaces

GET /mtg/cards/:id takes the long ID from the set or search results. For Emrakul, the Exigent Doom it returned:

SourceFinishLowMidHighMarket
TCGplayer (USD)Normal$28.08$32.62$58.92$29.08
TCGplayer (USD)Foil$33.01$42.30$94.97$39.85

TCGplayer, updated 30 September 2026. Normal also had a direct low of $29.48; Foil's was null.

SourceFinishLowTrendAvgAvg 7d
CardMarket (EUR)Normal€14.30€16.27€34.79€31.97
CardMarket (EUR)Foil€19.50€18.78€59.99€58.32

CardMarket, updated 30 September 2026. The response also carries avg1 and avg30.

Those figures were stamped two days before the set's listed release date, so they are pre-release prices. The CardMarket avg and trend fields sit far apart for this card. They measure different things, so pick one and stick with it rather than mixing them. Our Pokémon price tracker tutorial uses the same approach for deciding which field to display.

Plan your request budget before you loop. The free tier is 100 requests/hour and 1,000/day, and every call counts, including the card lookups you make after a search.

Ultra Endpoints: Prices & Analytics

Ultra costs €29/month, with 7,000 requests/hour and 70,000/day. The trial lasts 7 days and you start it from the dashboard after you log in. The whole Magic analytics layer sits behind it. See Pro vs Ultra for the plan breakdown.

Prices for a whole set

GET /mtg/prices/24684 returns one row per card and finish, with source: "both" and a total of 1,226 rows for Reality Fracture. Each row has the TCGplayer and CardMarket figures side by side:

{
  "name": "Emrakul, the Exigent Doom",
  "variant": "Foil",
  "tcgplayer": { "market_price": 39.85, "updated_at": "2026-09-30T20:37:36.812579+00:00" },
  "cardmarket": { "trend": 18.78, "avg": 59.99, "updated_at": "2026-09-30T14:49:39.980453+00:00" }
}

The row count (1,226) is higher than the card count because foils and other finishes get their own rows.

Statistics

GET /mtg/sets/24684/statistics summarises the set on each marketplace:

TCGplayer (USD)CardMarket (EUR)
Average price17.01 (avg_market_price)12.32
Minimum0.120.03
Maximum880452.25
Most expensiveOmnipresence (Borderless) (Facet Foil)Hexhaven Invigorator (Borderless) (Facet Foil)

CardMarket also reports a trend_direction, which was stable. The response includes a rarity breakdown (M, T, C, R, U, L). It also shows a total_cards of 767, where the set endpoint reported 782. The two endpoints count differently, so don't reconcile them and don't assume one is a bug in your code.

Completion value

GET /mtg/sets/24684/completion-value estimates what one copy of every card costs:

LowMarket / avgHigh / trend
TCGplayer (USD)18,286.8114,069.58 (market)31,790.08 (high)
CardMarket (EUR)11,034.0612,255.16 (avg)652.20 (trend)

The CardMarket trend_estimate of 652.20 is nowhere near its avg_estimate, and the response doesn't say why. Use one field consistently. The note in the response is explicit that this is one copy per card in the base finish, and that USD and EUR can't be compared directly.

GET /mtg/analytics/top-cards defaults to the highest prices from TCGplayer:

CardSetPrice
Smaug the Magnificent (Borderless)The Hobbit (HOB)24,500
Time WalkUnlimited Edition (2ED)5,719.99

Add ?metric=growth&source=cm to rank by CardMarket growth instead. The sample returned Sandstorm Eidolon (Dissension, €14.43, +753.8% over 7 days) and Aurora Eidolon (+479.2%, €15.87). Growth lists like this surface cheap commons with small supply, so check the sale volume behind a spike before acting on it.

GET /mtg/sets/trending covers a 7-day window. WCD 2002: Brian Kibler showed +112.1% on CardMarket (average €3.79 to €8.04), and Alpha Edition showed +78.9% (€519.91 to €930.31), with Timetwister up 191.4%. TCGplayer data was missing for both. The Alpha Edition entry returns work_in_progress with the message "TCGPlayer price history not yet available". Code defensively: the tcgplayer key can be null or a status object.

Code Examples: JavaScript & Python

Both examples use only the routes, parameters and fields shown above. Set your key as POKEWALLET_API_KEY.

JavaScript (Node 18+): search, then compare marketplaces

const BASE = "https://api.pokewallet.io";
const headers = { "X-API-Key": process.env.POKEWALLET_API_KEY };

async function get(path) {
  const res = await fetch(`${BASE}${path}`, { headers });
  if (!res.ok) {
    throw new Error(`${path} failed: HTTP ${res.status}`);
  }
  return res.json();
}

const { results } = await get("/mtg/search?q=Emrakul");

for (const card of results) {
  const { name, set_name } = card.card_info;

  const tcg = (card.tcgplayer?.prices ?? []).map(
    (p) => `${p.sub_type_name} $${p.market_price}`
  );
  const cm = (card.cardmarket?.prices ?? [])
    .filter((p) => p.trend !== null)
    .map((p) => `${p.variant_type} €${p.trend}`);

  console.log(`${name} (${set_name})`);
  console.log("  TCGplayer market:", tcg.join(", ") || "no data");
  console.log("  CardMarket trend:", cm.join(", ") || "no data");
}

The optional chaining covers the cards that exist on only one marketplace. The trend !== null filter drops finishes with no trend value.

Python: Ultra set prices

import os
import requests

BASE = "https://api.pokewallet.io"
HEADERS = {"X-API-Key": os.environ["POKEWALLET_API_KEY"]}


def get(path: str) -> dict:
    res = requests.get(f"{BASE}{path}", headers=HEADERS, timeout=30)
    res.raise_for_status()
    return res.json()


payload = get("/mtg/prices/24684")  # Ultra or trial required

rows = [
    {
        "name": row["name"],
        "variant": row["variant"],
        "usd": row["tcgplayer"]["market_price"],
        "eur_trend": row["cardmarket"]["trend"],
    }
    for row in payload["data"]
    if row.get("tcgplayer") and row.get("cardmarket")
]

for row in sorted(rows, key=lambda r: r["usd"] or 0, reverse=True)[:5]:
    print(row)

Without Ultra, this call fails, so catch the error and fall back to the single-card endpoint. There are also an official TypeScript and Python SDK, with typed results and automatic pagination, and a hosted MCP server if you'd rather query prices from an AI client.

Strip the markup before you display card_text:

const clean = (text) => text?.replace(/<br>\s*/g, "\n").replace(/\r/g, "").trim();

How It Compares to the Alternatives

Scryfall is the obvious comparison, and it is a good one. It is free and it already returns USD and EUR prices for Magic cards. If you only build for Magic and one price per card is enough, it may be all you need.

The case for this API is narrower and more practical:

  • One key, twelve games. The same X-API-Key and the same response shape cover Pokémon, One Piece, Yu-Gi-Oh!, Lorcana, Riftbound and the rest. If your product spans several games, you write one client.
  • Both marketplaces in one response. TCGplayer and CardMarket arrive together, with CardMarket split by finish, so you don't reconcile two sources yourself.
  • The ID model stays the same. Switching /mtg/ for /lor/ or /rft/ is the whole migration. See the Lorcana and Riftbound guides for the same walkthrough.

What it does not do: no eBay data, no grading data, and no price history for Magic. History exists only for Pokémon. Prices are daily snapshots, not live feeds, so don't use it for second-by-second arbitrage.

My recommendation: if you only need Magic cards, start with whichever free source fits. If you plan to add a second game, or need CardMarket and TCGplayer side by side, use one multi-game key from day one and skip the rewrite later.

New sets show up in /mtg/sets as the catalog adds them. Mystery Booster: Commander Edition, due 9 November, is the sort of release you would look up by set_code once it appears. Check the live list instead of hard-coding a set ID.


Frequently Asked Questions

Is there a free Magic: The Gathering price API?

Yes. The PokéWallet free tier gives you 100 requests/hour and 1,000/day with no credit card. Single-card and search responses include TCGplayer and CardMarket prices. Prices for whole sets and analytics require Ultra or the 7-day trial.

Does the API have CardMarket prices for Magic?

Yes. Each card carries CardMarket prices in EUR, split into normal and foil, with low, trend, avg, avg1, avg7 and avg30. Some cards have only a low and null for the rest.

How often are MTG prices updated?

TCGplayer and CardMarket prices refresh once a day. The updated_at field on each price shows the last time that price changed, not the time you made the request.

How many Magic sets does the API cover?

The set list currently reports 514 sets, including promo and special sets. GET /mtg/sets returns them all with card counts, sealed counts, language and release date.

Do I need Ultra to get Magic prices?

Not for individual cards. Card lookups and search include prices on every plan. You need Ultra (€29/month) or the 7-day trial for prices of whole sets, statistics, completion value, trending sets and top cards. Pro is not enough for Magic.

Can I use one API key for Magic and other card games?

Yes. One key covers twelve games, from Pokémon and One Piece to Magic, Yu-Gi-Oh!, Lorcana and Riftbound, each under its own path such as /mtg/ or /lor/.


💬 Price disclaimer: All figures come from PokéWallet API responses captured on 2 November 2026. TCGplayer prices are in USD and CardMarket prices are in EUR, and the updated_at stamps inside them are from 30 September 2026. They are daily snapshots, not live quotes, and not financial advice.


Start Building with the PokéWallet API

Everything in this guide runs on the free tier up to the Ultra sections, and the same key works across the other eleven games. Get a key, search a card, and see both marketplaces in one response. Our multi-game API overview shows the rest of the catalog, and the Magic: The Gathering docs cover every parameter.

  • 🔑 One API key for 12 trading card games — Pokémon, One Piece, Magic, Yu-Gi-Oh!, Lorcana, Riftbound and more
  • 💰 TCGplayer (USD) and CardMarket (EUR) prices, refreshed daily
  • 🆓 Catalog endpoints free on every plan — sets, cards, search and images
  • ⚡ Ultra unlocks prices and analytics for every game — try it free for 7 days from your dashboard
  • 💬 Developer support in our Discord

Get Started:


This article is for informational purposes only and is not financial advice. Details reflect information available at the time of writing; release dates, pricing and set contents are subject to change. Card prices are volatile — always verify current market pricing before buying or selling.