scryr is a lightweight wrapper around Scryfall, an amazing (and free!) Magic: The Gathering API. With scryr you can ingest card data as tidy data frames, allowing for frictionless integration with tidyverse pipelines.
Currently there are 2 main endpoints: cards
and
sets
. The other 4 endpoints (bulk_data
,
catalogs
, rulings
, and symbols
)
are mostly auxiliary to the main ones.
This endpoints is, by far, the most complex. Make sure to read
scry_cards()
’ full documentation before diving in! For the
curious, more relevant information about cards can be found in
vignette("syntax")
(Query Syntax) and
vignette("layouts")
(Layouts and Faces).
The most important function here is scry_cards()
. It
returns a data frame of card data given a query:
# Legendary vampires
vampires <- scry_cards("t:vampire t:legend")
# There are many, many columns
print(vampires)
#> # A tibble: 84 × 71
#> id name set lang colors color_identity mana_cost cmc oracle_text
#> <chr> <chr> <chr> <chr> <list> <list> <chr> <dbl> <chr>
#> 1 9acf80a5… Amal… lci en <chr> <chr [2]> {W}{B} 2 "Ward—Pay …
#> 2 462e887d… Anhe… ncc en <chr> <chr [3]> {U}{B}{R} 3 "Deathtouc…
#> 3 913dd06f… Anje… c19 en <chr> <chr [2]> {1}{B}{R} 3 "Haste\n{T…
#> 4 1bfac4ab… Anje… vow en <chr> <chr [2]> {2}{B}{R} 4 "Whenever …
#> 5 b8630ae1… Anow… voc en <chr> <chr [1]> {3}{B}{B} 5 "At the be…
#> 6 bca84fc4… Anow… znc en <chr> <chr [2]> {2}{U}{B} 4 "Other Rog…
#> 7 475d3d3a… Arva… moc en <chr> <chr [2]> {3}{W}{B} 5 "Deathtouc…
#> 8 a9c789e1… Arva… ydmu en <chr> <chr [2]> {W}{B} 2 "Deathtouc…
#> 9 213ad4ba… Asce… hop en <chr> <chr [1]> {4}{B}{B} 6 "Flying (T…
#> 10 64d932b9… Asta… clb en <chr> <chr [2]> {4}{W}{B} 6 "Deathtouc…
#> # ℹ 74 more rows
#> # ℹ 62 more variables: power <chr>, toughness <chr>, type_line <chr>,
#> # edhrec_rank <int>, keywords <list>, layout <chr>, legalities <list>,
#> # oversized <lgl>, reserved <lgl>, oracle_id <chr>, arena_id <int>,
#> # mtgo_id <int>, multiverse_ids <list>, tcgplayer_id <int>,
#> # cardmarket_id <int>, uri <chr>, scryfall_uri <chr>, rulings_uri <chr>,
#> # prints_search_uri <chr>, artist <chr>, artist_ids <list>, booster <lgl>, …
Note that many columns are list-columns with deeply nested information inside. This is a result of Scryfall’s data model and is the reason why scryr needs tibbles to work. But don’t be alarmed! It’s all pretty consistent.
# Get Anje's related cards
vampires %>%
filter(name == "Anje, Maid of Dishonor") %>%
pull(all_parts)
#> [[1]]
#> # A tibble: 2 × 6
#> object id component name type_line uri
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 related_card a6f374bc-cd29-469f-808a-6a6c004e… token Blood Token Ar… http…
#> 2 related_card d4dd1ee6-a9da-4c89-a3b8-b293ac28… combo_pi… Anje… Legendar… http…
# Get Anje's color identity
vampires %>%
filter(name == "Anje Falkenrath") %>%
pull(color_identity)
#> [[1]]
#> [1] "B" "R"
There are also “singular” functions, that is, functions that return
one card instead of many. They are scry_card()
and its
siblings, all of them methods that find a card given some sort of
identifier.
# Using an ID
scry_card("913dd06f-ed2f-4128-9c9d-9cd0d8a55425")$name
#> [1] "Anje Falkenrath"
# Using a name
scry_card_name("Anje Falkenrath")$name
#> [1] "Anje Falkenrath"
# Using a collector number and a set
scry_card_number(37, "c19")$name
#> [1] "Anje Falkenrath"
# Just get a random vampire commander
scry_card_random("t:vampire t:legend")$name
#> [1] "Amalia Benavides Aguirre"
If you’re unsure of exactly what card you’re looking for, don’t worry. Scryfall also has an endpoint that tries to autocomplete the name of a card and scryr makes it available so that you don’t have to ever leave R to look for a card.
The other main endpoint retrieves information about sets. There are
also many list-columns but, again, they are all handled consistently;
following in the footsteps of cards
, sets
also
has a “plural” function and a “singular” function. Note that
scry_cards()
is the only “plural” method that can filter
results with its q
argument.
# Get all sets
scry_sets()
#> # A tibble: 935 × 20
#> id code name mtgo_code arena_code uri scryfall_uri search_uri
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 23464cc2-a69c… dft Aeth… dft dft http… https://scr… https://a…
#> 2 b9618c8c-9f31… inr Inni… <NA> <NA> http… https://scr… https://a…
#> 3 053b14b2-b622… pspl Spot… <NA> <NA> http… https://scr… https://a…
#> 4 9cfdcd69-25f3… pfdn Foun… <NA> <NA> http… https://scr… https://a…
#> 5 3e036f87-f186… ffdn Foun… <NA> <NA> http… https://scr… https://a…
#> 6 9201567c-bab5… j25 Foun… <NA> <NA> http… https://scr… https://a…
#> 7 a7ecb771-d1b6… fdn Foun… fdn fdn http… https://scr… https://a…
#> 8 b229c424-54a9… fj25 Foun… <NA> <NA> http… https://scr… https://a…
#> 9 2a0b24ac-7614… fdc Foun… <NA> <NA> http… https://scr… https://a…
#> 10 cca2aac9-0d45… pltc Tale… <NA> <NA> http… https://scr… https://a…
#> # ℹ 925 more rows
#> # ℹ 12 more variables: released_at <date>, set_type <chr>, card_count <int>,
#> # digital <lgl>, nonfoil_only <lgl>, foil_only <lgl>, icon_svg_uri <chr>,
#> # parent_set_code <chr>, tcgplayer_id <int>, block_code <chr>, block <chr>,
#> # printed_size <int>
# Get a single set with an ID
scry_set("vow")
#> # A tibble: 1 × 19
#> id code name mtgo_code arena_code tcgplayer_id uri scryfall_uri
#> <chr> <chr> <chr> <chr> <chr> <int> <chr> <chr>
#> 1 8144b676-569… vow Inni… vow vow 2862 http… https://scr…
#> # ℹ 11 more variables: search_uri <chr>, released_at <date>, set_type <chr>,
#> # card_count <int>, printed_size <int>, digital <lgl>, nonfoil_only <lgl>,
#> # foil_only <lgl>, block_code <chr>, block <chr>, icon_svg_uri <chr>
All other endpoints return way less information than the two above. Here is a short demonstration of what else you can do with the rest of scryr:
# Get information from a catalog
head(scry_catalog("keyword-actions"))
#> [1] "Seek" "Activate" "Attach" "Cast" "Counter" "Create"
# Get rulings for a card
scry_ruling("913dd06f-ed2f-4128-9c9d-9cd0d8a55425")
#> # A tibble: 1 × 4
#> oracle_id source published_at comment
#> <chr> <chr> <date> <chr>
#> 1 4dab6a96-4376-4aea-983d-406167993214 wotc 2019-08-23 If you discard a car…
# Get information about symbols
scry_symbols()
#> # A tibble: 36 × 11
#> symbol colors cmc loose_variant english gatherer_alternates transposable
#> <chr> <list> <dbl> <chr> <chr> <list> <lgl>
#> 1 {X} <NULL> 0 X X generic… <chr [2]> FALSE
#> 2 {Y} <NULL> 0 Y Y generic… <NULL> FALSE
#> 3 {Z} <NULL> 0 Z Z generic… <NULL> FALSE
#> 4 {0} <NULL> 0 0 zero mana <chr [1]> FALSE
#> 5 {½} <NULL> 0.5 ½ one-half … <chr [1]> FALSE
#> 6 {1} <NULL> 1 1 one gener… <chr [1]> FALSE
#> 7 {2} <NULL> 2 2 two gener… <chr [1]> FALSE
#> 8 {3} <NULL> 3 3 three gen… <chr [1]> FALSE
#> 9 {4} <NULL> 4 4 four gene… <chr [1]> FALSE
#> 10 {5} <NULL> 5 5 five gene… <chr [1]> FALSE
#> # ℹ 26 more rows
#> # ℹ 4 more variables: represents_mana <lgl>, appears_in_mana_costs <lgl>,
#> # funny <lgl>, svg_uri <chr>
# Parse mana costs
parse_cost("2g2")$cost
#> [1] "{4}{G}"
# Get names of bulk files
scry_bulk_files()$name
#> [1] "Oracle Cards" "Unique Artwork" "Default Cards" "All Cards"
#> [5] "Rulings"
# Download (and parse) bulk rulings
scry_bulk_file("Rulings")
#> # A tibble: 68,119 × 4
#> oracle_id source published_at comment
#> <chr> <chr> <date> <chr>
#> 1 0004ebd0-dfd6-4276-b4a6-de0003e94237 wotc 2004-10-04 If there are two of…
#> 2 0007c283-5b7a-4c00-9ca1-b455c8dff8c3 wotc 2019-08-23 The “commander tax”…
#> 3 0007c283-5b7a-4c00-9ca1-b455c8dff8c3 wotc 2019-08-23 Certain cards in ot…
#> 4 0007c283-5b7a-4c00-9ca1-b455c8dff8c3 wotc 2019-08-23 If your commander i…
#> 5 000e5d65-96c3-498b-bd01-72b1a1991850 wotc 2004-10-04 The target loses ju…
#> 6 0012bc78-e69d-4a67-a302-e5fe0dfd4407 wotc 2019-05-03 A land normally has…
#> 7 00173df7-a584-410c-af1d-ada9c791056a wotc 2023-09-01 You can’t sacrifice…
#> 8 00173df7-a584-410c-af1d-ada9c791056a wotc 2023-09-01 Whatever you do, do…
#> 9 00173df7-a584-410c-af1d-ada9c791056a wotc 2023-09-01 Some spells and abi…
#> 10 00173df7-a584-410c-af1d-ada9c791056a wotc 2023-09-01 Food is an artifact…
#> # ℹ 68,109 more rows