API Reference – Scrollrack.gg

Scrollrack provides a simple and efficient REST API for validating trading card game decklists. This document details the endpoints, request/response formats, and usage examples.

Endpoint Overview

Method URL Description
POST /api/validate Validate a TCG decklist
POST /api/image Parse a decklist into card objects with images
GET /api/games Every game, its formats, and the zones each format accepts
GET /api/card/{game}/{id} One card by id: names, type, text, stats, image, legality
GET /api/search?game=&q= Prefix name search within a game

The GET endpoints are read-only and cacheable; they send Cache-Control and allow any origin (public card data). Card databases rebuild nightly, so a cached card is at most a day behind.


POST /api/validate

This endpoint validates a decklist against game-specific format rules.

Request

POST /api/validate
Content-Type: application/json

Request Body Parameters

Parameter Type Required Description
game string Yes Game identifier (mtg for Magic: The Gathering, pokemon for Pokémon)
format string Yes Format to validate against (e.g., commander for MTG, standard for Pokémon)
list string Yes Plain text decklist with section formatting

Example Request for MTG

{
  "game": "mtg",
  "format": "commander",
  "list": "~~Commanders~~\n1 Atraxa, Praetors' Voice\n\n~~Mainboard~~\n4 Sol Ring\n3 Cultivate\n92 Plains"
}

Example Request for Pokémon

{
  "game": "pokemon",
  "format": "standard",
  "list": "~~Pokemon~~\n4 Pikachu SVI 123\n2 Charizard PAL 45\n\n~~Trainer~~\n4 Boss's Orders FST 236\n\n~~Energy~~\n10 Basic Fire Energy"
}

Response

The API returns a JSON response with validation results.

Response Fields

Field Type Description
valid boolean Whether the deck is valid for the given format
format string The format that was validated against
errors array List of validation errors (only when valid is false)
decklist object Normalized decklist with card data (only when valid is true)
cardsPerSection object Count of cards in each section (only when valid is true)
totalCards number Total number of cards in the deck (only when valid is true)
colors array The deck's colors in the game's order — its identity card's (commander, Legend, Leader) when it has one, else the main deck's non-land cards. [] = colorless; absent for games without colors (only mtg, riftbound, onepiece, gundam carry them); null when the identity card is unknown. Returned for invalid lists too
meta object Format metadata including rules and requirements

Example Response - Valid MTG Deck

{
  "valid": true,
  "format": "commander",
  "decklist": {
    "commanders": {
      "Atraxa, Praetors' Voice": {
        "quantity": 1,
        "id": "bf6c1462-f1d2-4f58-b7a4-cb5b21399152"
      }
    },
    "mainboard": {
      "Sol Ring": {
        "quantity": 1,
        "id": "0afa0e33-4804-4b00-b625-c2d6b61090fc"
      },
      "Cultivate": {
        "quantity": 3,
        "id": "6e209ae9-7eca-4cf0-8070-c39a4da517f7"
      },
      "Plains": {
        "quantity": 95,
        "id": "3e9f26c9-64ec-4a23-a2a9-3eee8d0dcaac"
      }
    }
  },
  "cardsPerSection": {
    "commanders": 1,
    "mainboard": 99
  },
  "totalCards": 100,
  "meta": {
    "requiredSections": ["mainboard", "commanders"],
    "rules": {
      "requiredSections": ["mainboard", "commanders"],
      "deckSize": { "exact": 100 },
      "commanders": { "max": 2 },
      "mainboard": { "min": 98 },
      "maxCopies": 1
    }
  }
}

Example Response - Valid Pokémon Deck

{
  "valid": true,
  "format": "standard",
  "decklist": {
    "pokemon": {
      "Pikachu": {
        "quantity": 4,
        "set_id": "SVI 123",
        "id": "abc123"
      },
      "Charizard": {
        "quantity": 2,
        "set_id": "PAL 45",
        "id": "def456"
      }
    },
    "trainer": {
      "Boss's Orders": {
        "quantity": 4,
        "set_id": "FST 236",
        "id": "ghi789"
      }
    },
    "energy": {
      "Fire Energy": {
        "quantity": 10,
        "set_id": null,
        "id": "jkl012"
      }
    }
  },
  "cardsPerSection": {
    "pokemon": 6,
    "trainer": 4,
    "energy": 10
  },
  "totalCards": 20,
  "meta": {
    "requiredSections": ["pokemon", "trainer", "energy"],
    "rules": {
      "requiredSections": ["pokemon", "trainer", "energy"],
      "deckSize": { "exact": 60 },
      "maxCopies": 4
    }
  }
}

Example Response - Invalid Deck

{
  "valid": false,
  "errors": [
    "Too Many Copies Of: Mana Crypt (4 > 1)",
    "Use of Illegal Card: Mana Crypt"
  ],
  "format": "commander",
  "meta": {
    "requiredSections": ["mainboard", "commanders"],
    "rules": {
      "requiredSections": ["mainboard", "commanders"],
      "deckSize": { "exact": 100 },
      "commanders": { "max": 2 },
      "mainboard": { "min": 98 },
      "maxCopies": 1
    }
  }
}

Decklist Format

Decklists should be structured using section markers with the following format:

~~Section Name~~
[quantity] Card Name

Section Names by Game

Magic: The Gathering

Pokémon

Gundam Card Game

Card Format

Cards can be formatted in several ways:

Magic: The Gathering

4 Lightning Bolt   <- Quantity followed by card name
Lightning Bolt     <- Quantity defaults to 1 if omitted

Pokémon

4 Pikachu SVI 123   <- Quantity, set code, card number, and name
2 Basic Fire Energy <- Energy cards can be specified by name

Supported Games and Formats

Magic: The Gathering (mtg)

Formats:

Pokémon (pokemon)

Formats:

Gundam Card Game (gundam)

Formats:

Error Types

The API can return various error types:

  1. Missing Sections: When required sections are missing

    • Example: "Missing required section: ~~Commanders~~"
  2. Deck Size Violations: When deck size doesn't meet format requirements

    • Example: "Deck must contain exactly 100 cards (mainboard + commanders). Currently: 65"
  3. Section Constraints: When a section violates its size constraints

    • Example: "Mainboard must have at least 98 cards. Currently: 45"
  4. Card Quantity Violations: When a card exceeds its copy limit

    • Example: "Too Many Copies Of: Lightning Bolt (5 > 4)"
  5. Legality Violations: When a card is not legal in the format

    • Example: "Use of Illegal Card: Black Lotus"
  6. Unknown Cards: When a card name cannot be found in the database

    • Example: "Unknown Card: Made Up Card Name"
  7. Card Name Mismatch (Pokémon only): When the provided name doesn't match the database name for the given set/number

    • Example: "Card name mismatch: "Pkachu" does not match "Pikachu""

GET /api/games

Lists every supported game with its format keys and, per format, the zones a decklist may carry.

GET /api/games

Example Response

[
  {
    "code": "yugioh",
    "name": "Yu-Gi-Oh!",
    "formats": {
      "tcg": {
        "zones": [
          { "zone": "Main", "header": "Deck", "min": 40, "max": null, "required": true },
          { "zone": "Side", "header": "Side", "min": null, "max": 15, "required": false },
          { "zone": "Extra", "header": "Extra", "min": null, "max": 15, "required": false }
        ]
      }
    }
  }
]

zone is one of Main, Side, Extra, Leader, Viceroy, Locations, Attractions, Tribe. header is the section title the parser expects in a text list.


GET /api/card/{game}/{id}

One card by its entity id — the id returned in /api/image output and by /api/search. For Magic this is the Scryfall oracle id; for Yu-Gi-Oh the passcode; for Bandai games the card number.

GET /api/card/mtg/e6d09f4b-9c8d-4f57-8bd8-0be1b4c2ea7d

Response Fields

Field Type Description
id string Entity id
name string English name when stored, else the first stored name
names array Every stored name with its language code
type string | null The card's kind as the parser labels it (e.g. Creature, Monster, Spell)
typeLine string | null Printed type line (Magic type line, Yu-Gi-Oh card type, Bandai category and traits, …)
text string | null Rules text
stats string | null Combat stats as printed: power/toughness or loyalty, ATK/DEF, power, AP/HP, HP, might
tribe string | null Game-specific grouping (Magic colour identity, Bandai colour)
points number | null Points value in point-based formats
imgUrl string | null Card image
legality object Format key → Legal, NotLegal, Banned, Restricted, or Halved
data object The game's raw extra columns (e.g. CMC, ManaCost, Level, Attribute)

Returns 404 for an unknown game or id.


GET /api/search

Prefix search over card names within one game. Every whitespace-separated term must prefix a word in the name; matching ignores case and accents.

GET /api/search?game=mtg&q=thassa%20ora&limit=10
Parameter Required Description
game Yes Game code from /api/games
q Yes At least 2 characters
limit No 1–50, default 20

Example Response

[
  { "id": "…", "name": "Thassa's Oracle", "imgUrl": "https://images.topdeck.gg/game-images/mtg/….webp" }
]

One row per card, ordered by match rank.


Rate Limiting

The API currently does not implement rate limiting, but excessive usage may be throttled in the future.