MTG Finance Signals API

FreeNo keyOpen CORSCached

Use Scryfall for card objects and oracle text. Use SpellBook Finance for the derived market signals it does not compute: price movers, all-time-high breaks, set value trends, RSS feeds, and sealed EV rankings. All public JSON and RSS, no API key, no sign-up, open CORS.

Quick start: today's all-time-high breaks

const res = await fetch("https://api.spellbook-finance.com/ath");
const { data } = await res.json();
console.log(data.surpassedAth.slice(0, 5));

What this is, and what it is not

SpellBook Finance is not a card catalog replacement. It exposes derived finance data built from market history, sealed EV calculations, and set-level price snapshots. Endpoints send long Cache-Control headers and refresh on daily and hourly schedules, not per second.

Market data
The signals that move the MTG secondary market, recomputed daily and hourly.
GET/movers?timeframe=daily

Biggest price gainers and losers, computed from SpellBook price-history snapshots. timeframe = daily or weekly.

Returns { data: { gainers: [], losers: [] } }

GET/ath

Cards that just passed, or are near, their all-time-high price. ATH detection runs on SpellBook's full historical series (not Scryfall's current price).

Returns { data: { surpassedAth: [], nearAth: [] } }

GET/ticker

A compact market ticker of the day's notable movers.

Returns { data: [{ name, price, changePct, direction }] }

GET/buyouts

Cards detected spiking or getting bought out in the last 7 days, scored from SpellBook market price history.

Returns { buyouts: [{ name, setCode, oldPrice, newPrice, pctGain, detectedAt }] }

Sealed and box EV
Per-printing expected value for sealed product, the same numbers behind the EV rankings.
GET/embed/ev/rankings?count=10

Top booster boxes ranked by EV ratio. count = 1 to 25.

Returns { data: { rankings: [{ boxId, boxName, totalEV, evRatio, marketPrice }] } }

GET/embed/ev/{boxId}

EV for a single box by tcgProductId (get an id from the rankings above), with notable cards and eBay sold-comp fields when covered.

Returns { data: { boxId, boxName, totalEV, evRatio, marketPrice, spreadUsd, notableCards: [{ ebayMedianUsd, ebaySaleCount }] } }

GET/embed/sold-comps/{cardId}

Recent eBay sold median and resale anchor for one Scryfall card id or SpellBook card slug. Missing coverage returns a labeled empty payload.

Returns { data: { cardId, oracleId, dataState, ebayMedianUsd, ebaySaleCount, resaleAnchorUsd, resaleAnchorSource } }

Sets
Catalog data for every MTG set.
GET/sets

Every MTG set with codes and release info.

Returns { data: [...] }

GET/sets/{setCode}

One set plus its card list.

Returns { data: {...} }

Subscribe feeds (RSS)
Standards-compliant RSS for feed readers and Discord RSS bots. No polling code required.
GET/feeds/movers.xml

Daily gainers and losers as an RSS feed.

Returns application/rss+xml

GET/feeds/buyouts.xml

Buyout and spike detections as an RSS feed.

Returns application/rss+xml

Use it in a spreadsheet
Track your collection in Google Sheets? Paste this once under Extensions, Apps Script, and you get a live price function. No add-on to install, no key.
/**
 * SpellBook Finance live MTG price.
 * Usage:  =SPELLBOOK("Ragavan, Nimble Pilferer")
 *         =SPELLBOOK("Lightning Bolt", "MH2")   // pin a set
 */
function SPELLBOOK(name, setCode) {
  if (!name) return "";
  var url = "https://api.spellbook-finance.com/cards/find?q=" +
    encodeURIComponent(name);
  var res = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  var rows = (JSON.parse(res.getContentText()).data) || [];
  if (setCode) {
    rows = rows.filter(function (c) {
      return (c.setCode || "").toLowerCase() === String(setCode).toLowerCase();
    });
  }
  return rows.length ? Number(rows[0].priceUsd) : "";
}

Then use it like any formula: =SPELLBOOK("Ragavan, Nimble Pilferer") or pin a printing with =SPELLBOOK("Lightning Bolt", "MH2"). It returns the current market price in USD. Sheets caches custom functions, so it stays light on recalcs.

Using the API
  • Every endpoint is GET, returns JSON (or RSS for feeds), and needs no key.
  • CORS is open, so you can call these directly from browser JavaScript.
  • Responses send long Cache-Control headers, so cache on your side and avoid hammering; the data refreshes on daily and hourly schedules, not per second.
  • The public feed and embed endpoints carry a generous per-IP rate limit (120 requests/minute). A 429 with a Retry-After header means slow down and try again shortly.
  • A link back to spellbook-finance.com is appreciated when you publish something built on this data.
  • Want ready-made surfaces instead of raw data? See SpellBook Live for the stream overlay and embeddable widgets.