A simple (unofficial) wrapper for the Scryfall API.
npm install --save scryfall-client
The module is a singleton object that can make requests to the Scryfall API.
import scryfall from "scryfall-client";
When using on a node server, you must also set the User Agent (a requirement from the Scryfall API). When used in the browser, no other configuration is required, the browser's user agent will be used instead of your custom one.
scryfall.setUserAgent("MyPackage/1.2.3");
// now you can use the scryfall-client package
For the most part, this is a wrapper around the Scryfall API that provides convenience methods for the resulting API objects.
The goal is to provide a convenient way to get data from the Scryfall API in a succinct manner. For the most part, each API object will mirror the documented properties on the Scryfall API and include a set of helper methods to make navigating the data easier. If your project uses TypeScript, the response objects will be typed according to Scryfall's documentation.
The one key difference between the response objects returned from the raw API and this module is that for endpoints that return the list object, the raw API returns an object with some properties about the list (has_more
, next_page
, total_cards
) and a data
property that is an array of other API objects (cards, prints, rulings, etc). This module returns an Array-like object of the data directly, with the properties attached to the object. Similarly, endpoints that return the catalog object returns an Array-like object containing the data, as well as the other properties on the catalog.
scryfall.search("o:vigilance t:equipment").then(function (list) {
list.has_more; // whether or not there is an additional page of results, `true` or `false`
list.total_cards; // the total number of cards returned from search
var names = list.map(function (card) {
// the list object can use any Array method
return card.name;
});
});
If your request returns no results or is otherwise unsuccessful, the Promise will reject.
scryfall
.search("foobarbaz")
.then(function (list) {
// will never get here
})
.catch(function (err) {
err; // a 404 error
});
The other responses (Card
, MagicSet
, etc) are wrappers around the objects and any devitations or additional methods are noted in the Models section.
Perform a Scryfall search, where searchString
is the q
parameter.
scryfall.search("o:vigilance t:equipment").then(function (list) {
list.has_more; // whether or not there is an additional page of results, `true` or `false`
list.total_cards; // the total number of cards returned from search
var names = list.map(function (card) {
// the list object can use any Array method
return card.name;
});
});
You can pass additional query params as an options object. format
and pretty
are not supported.
scryfall
.search("o:vigilance t:equipment", {
unique: "prints",
order: "usd",
dir: "desc",
include_extras: true,
include_multilingual: true,
include_variations: true,
page: 2,
})
.then(function (list) {
// do something with list of cards
});
autocomplete(searchString: string, options?: object)
-> Promise<Catalog>
Perform a Scryfall autocomplete search, where searchString
is the q
parameter.
scryfall.autocomplete("Thal").then(function (list) {
// [
// "Thallid",
// "Thalakos Seer",
// "Thalakos Scout",
// "Thalakos Sentry",
// "Thalia's Lancers",
// "Thallid Devourer",
// "Thallid Omnivore",
// "Thalakos Drifters",
// "Thalakos Mistfolk",
// "Thalakos Lowlands",
// "Thalakos Deceiver",
// "Thallid Soothsayer",
// "Thallid Germinator",
// "Thalia's Lieutenant",
// "Thalia's Geistcaller",
// "Thallid Shell-Dweller",
// "Thalia, Heretic Cathar",
// "Thalakos Dreamsower",
// "Thalia, Guardian of Thraben",
// "Thorn Thallid"
// ]
});
You can pass additional query params as an options object. format
and pretty
are not supported.
scryfall
.search("o:vigilance t:equipment", {
unique: "prints",
order: "usd",
dir: "desc",
include_extras: true,
include_multilingual: true,
include_variations: true,
page: 2,
})
.then(function (list) {
// do something with list of cards
});
getCard(idOrName: string, kind?: string = "scryfall")
-> Promise<Card>
You can get a card object through a variety of API routes. By default, just passing an ID will return a card by looking up the Scryfall ID.
Available kind
options are:
id
(alias forscryfall
)scryfall
multiverse
arena
mtgo
(Magic Online ID)tcg
(TCG Player ID)fuzzyName
name
(alias forfuzzyName
)exactName
Find card by Scryfall id:
scryfall.getCard("scryfall-id").then(function (card) {
// do something with card
});
// can also be expicit about it by providing a kind
scryfall.getCard("scryfall-id", "scryfall").then(function (card) {
// do something with card
});
Find card by multiverse id:
scryfall.getCard(1234, "multiverse").then(function (card) {
// do something with card
});
Find card by arena id:
scryfall.getCard(1234, "arena").then(function (card) {
// do something with card
});
Find card by Magic Online id:
scryfall.getCard(1234, "mtgo").then(function (card) {
// do something with card
});
Find card by TCG Player id:
scryfall.getCard("id", "tcg").then(function (card) {
// do something with card
});
Find card by fuzzy name:
scryfall.getCard("fuzzy name", "fuzzyName").then(function (card) {
// do something with card
});
Alternatively, just use name
alias:
scryfall.getCard("fuzzy name", "name").then(function (card) {
// do something with card
});
Find card by exact name:
scryfall.getCard("exact name", "exactName").then(function (card) {
// do something with card
});
getCardNamed(name: string, options?: object)
-> Promise<Card>
scryfall.getCardNamed("fuzzy name").then(function (card) {
// do something with card
});
Get exact name:
scryfall.getCardNamed("exact name", { kind: "exact" }).then(function (card) {
// do something with card
});
Get a card, but specify the printing based on set code:
scryfall.getCardNamed("teferi time", { set: "dom" }).then(function (card) {
// do something with card
});
random(searchString?: string)
-> Promise<Card>
Get a random card:
scryfall.random().then(function (card) {
// do something with card
});
Get a random card that matches a search string:
scryfall.random("rarity:mythic").then(function (card) {
// random card that has been printed at mythic
});
getCardBySetCodeAndCollectorNumber(setCode: string, collectorNumber: string, lang?: string)
-> Promise<Card>
Find a card by passing the set code and collector number.
scryfall
.getCardBySetCodeAndCollectorNumber("set code", "123")
.then(function (card) {
// do something with card
});
Optionally pass a language parameter:
scryfall
.getCardBySetCodeAndCollectorNumber("set code", "123", "es")
.then(function (card) {
// do something with card
});
Fetch all the cards:
scryfall.getCards().then(function (cards) {
// do something with cards
});
Fetch all the cards starting on a page other than 1:
scryfall.getCards(5).then(function (cards) {
// do something with cards
});
Perform a lookup for all the sets.
scryfall.getSets().then(function (sets) {
sets.forEach(function (set) {
// do something with set
});
});
getSet(setCodeOrScryfallId: string)
-> Promise<MagicSet>
Perform a lookup for a particular Magic set by the code or Scryfall ID.
scryfall.getSet("dom").then(function (set) {
set.name; // "Dominaria"
set.code; // "dom"
});
scryfall.getSet("2ec77b94-6d47-4891-a480-5d0b4e5c9372").then(function (set) {
set.name; // "Ultimate Masters"
set.code; // "uma"
});
getSetByTcgId(tcgId: number)
-> Promise<MagicSet>
Perform a lookup for a particular Magic set by the TCG Player ID.
scryfall.getSetByTcgId(1909).then(function (set) {
set.name; // "Amonkhet Invocations"
set.code; // "mp2"
});
getCollection(identifiers: object[])
-> Promise<Array<Card>>
Perform a Scryfall collections request, where identifiers
is the identifiers
parameter.
scryfall
.getCollection([
{
id: "683a5707-cddb-494d-9b41-51b4584ded69",
},
{
name: "Ancient Tomb",
},
{
set: "mrd",
collector_number: "150",
},
])
.then(function (collection) {
collection[0].name; // Lodestone Golem
collection[1].name; // Ancient Tomb
collection[2].name; // Chalice of the Void
});
Normally, the collection API endpoint restricts requests to 75 identifiers. This module will automatically batch the requests in increments of 75 identifiers and then resolve with a flattened array of cards (not a List object).
If the exact API call you're looking for is missing, you can make a get request directly to any of the API endpoints. It will return a Promise that resolves with the result.
scryfall.get("cards/random").then(function (card) {
card; // a random card
});
You can pass a second argument that will be converted to a query string:
scryfall
.get("cards/search", {
q: "o:vigilance t:equipment",
})
.then(function (list) {
list.forEach(function (card) {
console.log(card.name);
});
});
You can also call post
with a post body:
scryfall
.post("cards/collection", {
identifiers: [
{
id: "some-id",
},
{
set: "some-set-code",
collector_number: "some-collector-number",
},
],
})
.then(function (list) {
list.forEach(function (card) {
console.log(card.name);
});
});
As a convenience, you can call getSymbolUrl
with a symbol character to generate the Scryfall url for the symbols svg:
scryfall.getSymbolUrl("W"); // 'https://img.scryfall.com/symbology/W.svg'
scryfall.getSymbolUrl("{U}"); // 'https://img.scryfall.com/symbology/U.svg'
A function that yields the text of the fields of the card, whatever the function returns will replace the text.
The main use case is for transforming the mana symbol notation. You can use the getSymbolUrl
method in conjunction with the textTransformer function to embed the mana symbols on a web page.
scryfall.setTextTransform(function (text) {
var matches = text.match(/{(.)(\/(.))?}/g);
if (matches) {
matches.forEach(function (symbol) {
var key = symbol.slice(1, -1);
text = text.replace(
symbol,
'<img src="' + scryfall.getSymbolUrl(key) + '"/>'
);
});
}
return text;
},
});
scryfall
.get("cards/named", {
exact: "River Hoopoe",
})
.then(function (card) {
// Flying\n<img src="https://img.scryfall.com/symbology/3.svg" /><img src="https://img.scryfall.com/symbology/G.svg" /><img src="https://img.scryfall.com/symbology/U.svg" />: You gain 2 life and draw a card.
card.oracle_text;
});
It is probably easiest to copy the function above as is, and replace the second argument in text.replace
with your own string where $1
is the first match and $3
is the second match if the mana symbols is a split symbol (such as with hybrid mana).
If using this module within Slack, you may want the mana symbols converted automatically to the emoji that Scryfall provides.
scryfall.slackify();
scryfall.get("cards/random").then(function (card) {
card.mana_cost; // ':mana-2::mana-G:
});
If using this module within Discord, you may want the mana symbols converted automatically to the emoji that Scryfall provides.
scryfall.discordify();
scryfall.get("cards/random").then(function (card) {
card.mana_cost; // ':mana2::manaG:
});
To reset the text transformation to the defaults, use resetTextTransform
.
scryfall.resetTextTransform();
By default, there is about 50-100 milliseceond delay between requests (as recomended by Scryfall). You can configure this value.
scryfall.setApiRequestDelayTime(500);
scryfall
.get("cards/random")
.then(function (card) {
// do something with card
// will wait 500 milliseconds before initiating this request
return scryfall.get("cards/random");
})
.then(function (card) {
// do something with card
});
To reset the delay time back to the default, use resetApiRequestDelayTime
.
scryfall.resetApiRequestDelayTime();
As a convenience, there are a number of API objects with special methods.
Representing a card object. Normally, only double faced cards will have a card_faces
array, but the Card
instance will always include a card_faces
array, defaulting to the main attributes from the card if the card does not have multiple faces.
getRulings()
-> Promise<List>
Returns a Promise that resolves with a list of rulings objects
scryfall
.get("cards/named", {
fuzzy: "aust com",
})
.then(function (card) {
return card.getRulings();
})
.then(function (list) {
list.forEach(function (ruling) {
console.log(ruling.published_at);
console.log(ruling.comment);
});
});
getSet()
-> Promise<MagicSet>
Returns a Promise that resolves with the set object for the card.
scryfall
.get("cards/named", {
exact: "the Scarab God",
})
.then(function (card) {
return card.getSet();
})
.then(function (set) {
set.name; // the name of the set
});
Returns a Promise that resolves with a list of card objects for each printing of the card.
scryfall
.get("cards/named", {
exact: "windfall",
})
.then(function (card) {
return card.getPrints();
})
.then(function (list) {
var sets = list.map(function (card) {
return card.set;
});
});
Returns true or false for provided format. As of the writing of this documentation, the valid values are:
'standard'
'future'
'frontier'
'modern'
'legacy'
'pauper'
'vintage'
'penny'
'commander'
'1v1'
'duel'
'brawl'
As more formats are added, isLegal
will support them automatically (as it takes its list of valid values from the API response itself).
isLegal
will return true
if Scryfall lists the card as legal
or restricted
and false
otherwise.
scryfall
.get("cards/search", {
q: "format:standard r:r",
})
.then(function (list) {
var aCard = list[0];
aCard.isLegal("standard"); // true
aCard.isLegal("pauper"); // false
});
Returns the image url of the specified size. Defaults to the normal
size.
As of the writing of this documentation, the valid values are:
'small'
'normal'
'large'
'png'
'art_crop'
'border_crop'
If additional formats are added, getImage
will support them automatically (as it takes its list of valid values from the API response itself).
scryfall
.get("cards/named", {
exact: "windfall",
})
.then(function (card) {
const img = card.getImage();
img; // set an img tag's src to this
});
Returns the image url of the back of the card. In almost all cases, this will return Scryfall's URL for the backside of a card. For transform cards. It will return the image url for the back face of the card.
The default format parameter is 'normal'
. As of the writing of this documentation, the valid values are:
'small'
'normal'
'large'
'png'
'art_crop'
'border_crop'
If additional formats are added, getBackImage
will support them automatically (as it takes its list of valid values from the API response itself).
If a non-doublesided card is used with getBackImage
, the size parameter will be ignored.
// A Magic card without a back face
scryfall
.get("cards/named", {
exact: "windfall",
})
.then(function (card) {
const img = card.getBackImage(); // http://cards.scryfall.io/back.png
});
// A transform card
scryfall
.get("cards/named", {
exact: "docent of perfection",
})
.then(function (card) {
const img = card.getBackImage(); // the img url for Final Iteration
});
Returns a string with the specifed price. If the price is not available for the specified type, ''
will be returned.
If no type is specified, it will return the price for 'usd'
if available. If 'usd'
is not available, 'usd_foil'
will be used. If 'usd_foil'
is not available, 'eur'
will be used. If 'eur'
is not available, 'tix'
will be used. If 'tix'
is not available, ''
will be returned.
scryfall
.get("cards/named", {
fuzzy: "animar soul",
})
.then(function (card) {
card.getPrice(); // '11.25'
card.getPrice("usd"); // '11.25'
card.getPrice("usd_foil"); // '52.51'
});
Returns a Promise that resolves with a list of card objects for each token associated with the card.
scryfall
.get("cards/named", {
exact: "Bestial Menace",
})
.then(function (card) {
return card.getTokens();
})
.then(function (list) {
const imgs = list.map(function (card) {
return card.getImage();
});
imgs.forEach(function (img) {
// display image
});
});
Returns a url for the tagger page for the card. This is derived from the card attributes based on the structure of the current tagger urls. If the structure changes, this method will no longer point to the correct url.
scryfall
.get("cards/named", {
exact: "Krenko, Mob Boss",
})
.then(function (card) {
return card.getTaggerUrl();
})
.then(function (url) {
url; // https://tagger.scryfall.com/card/ddt/52
});
An object representing a catalog object. This is an Array like object where the entries are the data
attribute from the raw API. The rest of the properties are present on the Catalog
.
An object representing a list object. This is an Array like object where the entries are the data
attribute from the raw API. The rest of the properties are present on the List
.
If the has_more
property is true
, then next()
can be called to get the next page of results.
function collectCards(list, allCards) {
allCards = allCards || [];
allCards.push.apply(allCards, list);
if (!list.has_more) {
return allCards;
}
return list.next().then(function (newList) {
return collectCards(newList, allCards);
});
}
scryfall
.get("cards/search", {
q: "format:standard r:r",
})
.then(function (list) {
return collectCards(list);
})
.then(function (allRareCardsInStandard) {
// do something!!
});
An object represnting a set object.
Resolves with a list containing all the cards in the set.
scryfall
.get("sets/dom")
.then(function (set) {
return set.getCards();
})
.then(function (list) {
list; // a list of cards for the set
});
Any other objects are wrapped in a GenericScryfallResponse
object.
The instance includes a wrap
method which can be used to wrap a saved respone from Scryall into the API objects listed above. For instance, you may want to convert a Card object into a JSON string to save to your database; this method allows you to rebuild the Card object with all the helper methods without fetching it again from Scryfall.
scryfall.get("cards/random").then(function (card) {
saveCardToDatabse(card);
});
// later
lookUpCardInDatabase(someId).then(function (cardData) {
// cardData.getImage does not exist
const card = scryfall.wrap(cardData);
const img = card.getImage(); // the image of the card saved in the database
});
The source code is written in Typescript and transpiled to ES5.
The module makes use of the Promise object, so if this SDK is used in a browser that does not support promises, it will need to be polyfilled in your script.
The code base uses Prettier. Run:
npm run pretty
To lint and run the unit tests, simply run:
npm test
To run just the unit tests, run:
npm run test:unit
To run just the linting command, run:
npm run lint
To run the integration tests, run:
npm run test:integration
If you find a bug, feel free to open an issue or a Pull Request.
The same goes for missing Typescript properties. If the Scryfall API adds a new property, open an issue or PR to add it to the module.