Skip to content

mtgjson/mtgjson-sdk-r

Repository files navigation

mtgjsonsdk

The official MTGJSON SDK for R — a high-performance, DuckDB-backed query client.

Unlike traditional SDKs that rely on rate-limited REST APIs, mtgjsonsdk implements a local data warehouse architecture. It synchronizes optimized Parquet data from the MTGJSON CDN to your local machine, utilizing DuckDB to execute complex analytics, fuzzy searches, and booster simulations with sub-millisecond latency.

Key Features

  • Vectorized Execution: Powered by DuckDB for high-speed OLAP queries on the full MTG dataset.
  • Offline-First: Data is cached locally, allowing for full functionality without an active internet connection.
  • Fuzzy Search: Built-in Jaro-Winkler similarity matching to handle typos and approximate name lookups.
  • Native Data Frames: All queries return standard R data.frames — ready for dplyr, ggplot2, or any tidyverse workflow.
  • R6 Class System: Clean, object-oriented API with lazy-loaded query interfaces and method chaining.
  • Booster Simulation: Accurate pack opening logic using official MTGJSON weights and sheet configurations.

Install

# install.packages("devtools")
devtools::install_local("path/to/mtgjson-sdk-r")

Quick Start

library(mtgjsonsdk)

sdk <- MtgjsonSDK$new()

# Search for cards (returns data.frames)
bolts <- sdk$cards$get_by_name("Lightning Bolt")
cat(sprintf("Found %d printings of Lightning Bolt\n", nrow(bolts)))

# Get set metadata
mh3 <- sdk$sets$get("MH3")
cat(sprintf("%s -- %s cards\n", mh3$name, mh3$totalSetSize))

# Check format legality
if (nrow(bolts) > 0) {
  cat(sprintf("Modern legal: %s\n", sdk$legalities$is_legal(bolts$uuid[1], "modern")))
}

# Find the cheapest printing
cheapest <- sdk$prices$cheapest_printing("Lightning Bolt")
if (!is.null(cheapest)) {
  cat(sprintf("Cheapest: $%s (%s)\n", cheapest$price, cheapest$setCode))
}

# Execute raw SQL with parameter binding
rows <- sdk$sql("SELECT name FROM cards WHERE manaValue = $1 LIMIT 5", list(0))

sdk$close()

Architecture

By using DuckDB, the SDK leverages columnar storage and vectorized execution, making it significantly faster than SQLite or standard JSON parsing for MTG's relational dataset.

  1. Synchronization: On first use, the SDK lazily downloads Parquet and JSON files from the MTGJSON CDN to a platform-specific cache directory (~/.cache/mtgjson-sdk on Linux, ~/Library/Caches/mtgjson-sdk on macOS, AppData/Local/mtgjson-sdk on Windows).
  2. Virtual Schema: DuckDB views are registered on-demand. Accessing sdk$cards registers the card view; accessing sdk$prices registers price data. You only pay the memory cost for the data you query.
  3. Dynamic Adaptation: The SDK introspects Parquet metadata to automatically handle schema changes, plural-column array conversion, and format legality unpivoting.
  4. Materialization: Queries return standard R data.frames, ready for use with dplyr, ggplot2, or base R.

Use Cases

Price Analytics

sdk <- MtgjsonSDK$new()

# Find the cheapest printing of a card by name
cheapest <- sdk$prices$cheapest_printing("Ragavan, Nimble Pilferer")

# Aggregate statistics (min, max, avg) for a specific card
trend <- sdk$prices$price_trend(
  cheapest$uuid, provider = "tcgplayer", finish = "normal"
)
cat(sprintf("Range: $%s - $%s\n", trend$min_price, trend$max_price))
cat(sprintf("Average: $%s over %d data points\n", trend$avg_price, trend$data_points))

# Historical price lookup with date filtering
history <- sdk$prices$history(
  cheapest$uuid,
  provider = "tcgplayer",
  date_from = "2024-01-01",
  date_to = "2024-12-31"
)

# Top 10 most expensive printings across the entire dataset
priciest <- sdk$prices$most_expensive_printings(limit = 10)

sdk$close()

Advanced Card Search

The search() method supports ~20 composable filters that can be combined freely:

sdk <- MtgjsonSDK$new()

# Complex filters: Modern-legal red creatures with CMC <= 2
aggro <- sdk$cards$search(
  colors = "R",
  types = "Creature",
  mana_value_lte = 2,
  legal_in = "modern",
  limit = 50
)

# Typo-tolerant fuzzy search (Jaro-Winkler similarity)
results <- sdk$cards$search(fuzzy_name = "Ligtning Bolt")  # still finds it

# Rules text search using regular expressions
burn <- sdk$cards$search(text_regex = "deals? \\d+ damage to any target")

# Search by keyword ability across formats
flyers <- sdk$cards$search(keyword = "Flying", colors = c("W", "U"), legal_in = "standard")

# Find cards by foreign-language name
blitz <- sdk$cards$search(localized_name = "Blitzschlag")  # German for Lightning Bolt

sdk$close()
All search() parameters
Parameter Type Description
name character Name pattern (% = wildcard)
fuzzy_name character Typo-tolerant Jaro-Winkler match
localized_name character Foreign-language name search
colors character Cards containing these colors
color_identity character Color identity filter
legal_in character Format legality
rarity character Rarity filter
mana_value numeric Exact mana value
mana_value_lte numeric Mana value upper bound
mana_value_gte numeric Mana value lower bound
text character Rules text substring
text_regex character Rules text regex
types character Type line search
artist character Artist name
keyword character Keyword ability
is_promo logical Promo status
availability character "paper" or "mtgo"
language character Language filter
layout character Card layout
set_code character Set code
set_type character Set type (joins sets table)
power character Power filter
toughness character Toughness filter
limit / offset integer Pagination

Collection & Cross-Reference

sdk <- MtgjsonSDK$new()

# Cross-reference by any external ID system
cards <- sdk$identifiers$find_by_scryfall_id("f7a21fe4-...")
cards <- sdk$identifiers$find_by_tcgplayer_id("12345")
cards <- sdk$identifiers$find_by_mtgo_id("67890")

# Get all external identifiers for a card
all_ids <- sdk$identifiers$get_identifiers("card-uuid-here")
# -> Scryfall, TCGPlayer, MTGO, Arena, Cardmarket, Card Kingdom, Cardsphere, ...

# TCGPlayer SKU variants (foil, etched, etc.)
skus <- sdk$skus$get("card-uuid-here")

# Export to a standalone DuckDB file for offline analysis
sdk$export_db("my_collection.duckdb")
# Now query with: duckdb my_collection.duckdb "SELECT * FROM cards LIMIT 5"

sdk$close()

Booster Simulation

sdk <- MtgjsonSDK$new()

# See available booster types for a set
types <- sdk$booster$available_types("MH3")  # c("draft", "collector", ...)

# Open a single draft pack using official set weights
pack <- sdk$booster$open_pack("MH3", "draft")
for (i in seq_len(nrow(pack))) {
  cat(sprintf("  %s (%s)\n", pack$name[i], pack$rarity[i]))
}

# Simulate opening a full box (36 packs)
box <- sdk$booster$open_box("MH3", "draft", packs = 36L)
cat(sprintf("Opened %d packs, %d total cards\n",
    length(box), sum(vapply(box, nrow, integer(1)))))

sdk$close()

API Reference

Core Data

# Cards
sdk$cards$get_by_uuid("uuid")                # single card lookup
sdk$cards$get_by_uuids(c("uuid1", "uuid2"))  # batch lookup
sdk$cards$get_by_name("Lightning Bolt")       # all printings of a name
sdk$cards$search(...)                         # composable filters (see above)
sdk$cards$get_printings("Lightning Bolt")     # all printings across sets
sdk$cards$get_atomic("Lightning Bolt")        # oracle data (no printing info)
sdk$cards$find_by_scryfall_id("...")          # cross-reference shortcut
sdk$cards$random(5)                           # random cards
sdk$cards$count()                             # total (or filtered with kwargs)

# Tokens
sdk$tokens$get_by_uuid("uuid")
sdk$tokens$get_by_name("Soldier Token")
sdk$tokens$search(name = "%Token", set_code = "MH3", colors = "W")
sdk$tokens$for_set("MH3")

# Sets
sdk$sets$get("MH3")
sdk$sets$list(set_type = "expansion")
sdk$sets$search(name = "Horizons", release_year = 2024)

Playability

# Legalities
sdk$legalities$formats_for_card("uuid")     # -> c(modern = "Legal", ...)
sdk$legalities$legal_in("modern")           # all modern-legal cards
sdk$legalities$is_legal("uuid", "modern")   # -> TRUE/FALSE
sdk$legalities$banned_in("modern")          # also: restricted_in, suspended_in

# Decks & Sealed Products
sdk$decks$list(set_code = "MH3")
sdk$decks$search(name = "Eldrazi")
sdk$sealed$list(set_code = "MH3")
sdk$sealed$get("uuid")

Market & Identifiers

# Prices
sdk$prices$get("uuid")                      # full nested price data
sdk$prices$today("uuid", provider = "tcgplayer", finish = "foil")
sdk$prices$history("uuid", provider = "tcgplayer", date_from = "2024-01-01")
sdk$prices$price_trend("uuid", provider = "tcgplayer", finish = "normal")
sdk$prices$cheapest_printing("Lightning Bolt")
sdk$prices$most_expensive_printings(limit = 10)

# Identifiers (supports all major external ID systems)
sdk$identifiers$find_by_scryfall_id("...")
sdk$identifiers$find_by_tcgplayer_id("...")
sdk$identifiers$find_by_mtgo_id("...")
sdk$identifiers$find_by_mtg_arena_id("...")
sdk$identifiers$find_by_multiverse_id("...")
sdk$identifiers$find_by_mcm_id("...")
sdk$identifiers$find_by_card_kingdom_id("...")
sdk$identifiers$find_by("scryfallId", "...")  # generic lookup
sdk$identifiers$get_identifiers("uuid")       # all IDs for a card

# SKUs
sdk$skus$get("uuid")
sdk$skus$find_by_sku_id(123456)
sdk$skus$find_by_product_id(789)

Booster & Enums

sdk$booster$available_types("MH3")
sdk$booster$open_pack("MH3", "draft")
sdk$booster$open_box("MH3", packs = 36L)
sdk$booster$sheet_contents("MH3", "draft", "common")

sdk$enums$keywords()
sdk$enums$card_types()
sdk$enums$enum_values()

System

sdk$meta                                    # version and build date
sdk$views                                   # registered view names
sdk$refresh()                               # check CDN for new data -> logical
sdk$export_db("output.duckdb")              # export to persistent DuckDB file
sdk$sql(query, params)                      # raw parameterized SQL
sdk$close()                                 # release resources

Performance and Memory

When querying large datasets (thousands of cards), the SDK returns standard R data.frames which integrate directly with the tidyverse. For bulk analysis, use raw SQL to let DuckDB handle aggregation natively rather than pulling large result sets into R.

# Aggregation runs in DuckDB's C++ engine, not R
result <- sdk$sql("
  SELECT setCode, COUNT(*) as card_count, AVG(manaValue) as avg_cmc
  FROM cards
  GROUP BY setCode
  ORDER BY card_count DESC
  LIMIT 10
")

Advanced Usage

SqlBuilder

SqlBuilder is exported for constructing parameterized DuckDB queries with method chaining. All user values go through $N parameter binding — never string interpolation.

library(mtgjsonsdk)

sdk <- MtgjsonSDK$new()

# Ensure views are registered before querying
sdk$cards$count()

q <- SqlBuilder$new("cards")$
  select("name", "setCode", "manaValue")$
  where_eq("rarity", "mythic")$
  where_gte("manaValue", "5")$
  where_like("name", "%Dragon%")$
  where_in("setCode", list("MH3", "LTR", "WOE"))$
  order_by("manaValue DESC", "name ASC")$
  limit(25)$
  build()

result <- sdk$sql(q$sql, q$params)

sdk$close()

Auto-Refresh for Long-Running Services

# In a scheduled task or health check:
if (sdk$refresh()) {
  message("New MTGJSON data detected -- cache refreshed")
}

Custom Cache Directory & Progress

on_progress <- function(filename, downloaded, total) {
  pct <- if (total > 0) downloaded / total * 100 else 0
  cat(sprintf("\r%s: %.1f%%", filename, pct))
}

sdk <- MtgjsonSDK$new(
  cache_dir = "/data/mtgjson-cache",
  timeout = 300,
  on_progress = on_progress
)

Raw SQL

All user input goes through DuckDB parameter binding ($1, $2, ...):

sdk <- MtgjsonSDK$new()

# Ensure views are registered before querying
sdk$cards$count()

# Parameterized queries
rows <- sdk$sql(
  "SELECT name, setCode, rarity FROM cards WHERE manaValue <= $1 AND rarity = $2",
  list(2, "mythic")
)

sdk$close()

Examples

Format Metagame Analyst (Shiny Dashboard)

An interactive Shiny dashboard for analyzing MTG format metagames — card pool sizes, ban lists, mana curves, color distributions, keyword frequency, creature P/T heatmaps, and token census across all major formats.

SDK features showcased: Legalities (legal_in, banned_in, restricted_in, suspended_in), SqlBuilder (custom aggregation queries), Enums (keywords, card_types), Tokens (for_set, search), Cards (search with keyword/type/mana value filters).

cd examples/format-analyst
Rscript install.R                    # install Shiny + ggplot2 + other deps
Rscript -e "shiny::runApp('.')"      # launch dashboard
Tab What it shows
Overview Legal card count, banned/restricted/suspended counts, format comparison bar chart, ban list table
Mana & Colors Color distribution, mana curve histogram, average mana value by color
Keywords & Types Top 20 keywords, card type donut chart, top creature subtypes, keyword reference panel
Power & Toughness P/T heatmap, average P/T by mana cost, "stat monsters" efficiency table
Token Census Tokens by set, most common tokens, token color distribution

Development

git clone https://github.com/mtgjson/mtgjson-sdk-r.git
cd mtgjson-sdk-r
Rscript -e "devtools::install_deps(dependencies = TRUE)"
Rscript -e "devtools::test()"
Rscript -e "devtools::check()"

License

MIT

About

No description, website, or topics provided.

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE
MIT
LICENSE.txt

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages