Card Kingdom APIcardkingdom.com ↗
Access Magic: The Gathering card prices, buylist rates, edition catalogs, and deals from Card Kingdom via 6 structured API endpoints.
What is the Card Kingdom API?
The Card Kingdom API provides structured access to Magic: The Gathering card data across 6 endpoints, covering retail prices, buylist rates, edition listings, and current deals. The search_cards endpoint lets you query cards by name, format, or edition and returns per-condition pricing and availability. Additional endpoints expose full card details, sell-to-store buylist prices in both cash and store credit, and a complete catalog of MTG sets.
curl -X GET 'https://api.parse.bot/scraper/57fad8b8-9bec-4fbf-9dfd-c5d328040d29/search_cards?query=Lightning+Bolt' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace cardkingdom-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
Card Kingdom MTG API Client
Comprehensive API for extracting Magic: The Gathering card data, pricing, and availability.
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Optional, Dict, Any, List
class ParseClient:
"""Client for the Card Kingdom MTG API via Parse.bot"""
def __init__(self, api_key: Optional[str] = None):
"""Initialize the Parse API client.
Args:
api_key: API key for Parse.bot. Falls back to PARSE_API_KEY env var.
"""
self.base_url = "https://api.parse.bot"
self.scraper_id = "57fad8b8-9bec-4fbf-9dfd-c5d328040d29"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key not provided and PARSE_API_KEY not set")
def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
"""Make an API call to the Parse endpoint.
Args:
endpoint: The endpoint name (e.g., "search_cards")
method: HTTP method (GET or POST)
**params: Query/body parameters for the request
Returns:
Response JSON as dictionary
Raises:
requests.exceptions.RequestException: If the request fails
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
if method.upper() == "GET":
response = requests.get(url, headers=headers, params=params)
else: # POST
response = requests.post(url, headers=headers, json=params)
response.raise_for_status()
return response.json()
def search_cards(
self,
query: Optional[str] = None,
edition: Optional[str] = None,
format: Optional[str] = None,
page: int = 1
) -> Dict[str, Any]:
"""Search for Magic: The Gathering cards by name, edition, and format.
Args:
query: Card name search keyword
edition: Filter by card set/edition name
format: Filter by tournament format (e.g., Standard, Commander, Legacy)
page: Page number for pagination (default: 1)
Returns:
Dictionary with page info and list of card items
"""
params = {"page": page}
if query:
params["query"] = query
if edition:
params["edition"] = edition
if format:
params["format"] = format
return self._call("search_cards", method="GET", **params)
def get_card_detail(self, url: str) -> Dict[str, Any]:
"""Get full details for a specific card listing.
Args:
url: The URL or path of the card detail page (e.g., /mtg/stronghold/mox-diamond)
Returns:
Dictionary with card details including name, oracle text, and conditions
"""
return self._call("get_card_detail", method="GET", url=url)
def get_buylist_prices(
self,
query: Optional[str] = None,
edition: Optional[str] = None,
page: int = 1
) -> Dict[str, Any]:
"""Retrieve buylist (sell-to-CK) prices for cards.
Args:
query: Card name search keyword
edition: Filter by set name
page: Page number (default: 1)
Returns:
Dictionary with page info and list of cards with buylist prices
"""
params = {"page": page}
if query:
params["query"] = query
if edition:
params["edition"] = edition
return self._call("get_buylist_prices", method="GET", **params)
def list_all_editions(self) -> Dict[str, List[Dict[str, str]]]:
"""List all available MTG editions/sets.
Returns:
Dictionary with list of editions containing name, slug, and url
"""
return self._call("list_all_editions", method="GET")
def get_deals(self, page: int = 1) -> Dict[str, Any]:
"""Get current discounted deals and on-sale products.
Args:
page: Page number (default: 1)
Returns:
Dictionary with page info and list of discounted items
"""
return self._call("get_deals", method="GET", page=page)
def get_formats(self) -> Dict[str, Dict[str, Any]]:
"""Get a list of all game formats and their IDs.
Returns:
Dictionary of format objects keyed by index with name, slug, and id
"""
return self._call("get_formats", method="GET")
def find_best_deal_across_conditions(card_details: Dict[str, Any]) -> Dict[str, Any]:
"""Find the cheapest available condition for a card.
Args:
card_details: Card details from get_card_detail
Returns:
Dictionary with best condition, price, and availability
"""
conditions = card_details.get("conditions", [])
if not conditions:
return {}
# Find cheapest available condition
best = None
for cond in conditions:
if int(cond.get("available", 0)) > 0:
price_str = cond.get("price", "$0").replace("$", "")
if best is None or float(price_str) < float(best["price"].replace("$", "")):
best = cond
return best or {}
def calculate_arbitrage_opportunity(retail_price: str, buylist_credit: str) -> float:
"""Calculate profit margin from buying at retail and selling to buylist.
Args:
retail_price: Card retail price as string (e.g., "$100.00")
buylist_credit: Buylist credit price as string (e.g., "$120.00")
Returns:
Profit margin percentage
"""
try:
retail = float(retail_price.replace("$", ""))
buylist = float(buylist_credit.replace("$", ""))
if retail == 0:
return 0
return ((buylist - retail) / retail) * 100
except (ValueError, AttributeError):
return 0
if __name__ == "__main__":
# Initialize client
client = ParseClient()
print("=" * 70)
print("Card Kingdom MTG API - Practical Workflow Example")
print("=" * 70)
# Step 1: Get all available formats to understand the market
print("\n[1] Fetching available game formats...")
formats_data = client.get_formats()
formats = formats_data.get("data", {})
print(f"Found {len(formats)} formats:")
for fmt_key, fmt in list(formats.items())[:5]:
print(f" - {fmt['name']} (slug: {fmt['slug']})")
# Step 2: Search for a valuable card (high-value targets)
print("\n[2] Searching for 'Mox' cards in inventory...")
search_result = client.search_cards(query="Mox", page=1)
cards = search_result.get("data", {}).get("items", [])
print(f"Found {len(cards)} Mox cards available:")
# Step 3: Analyze each card found - get details and check pricing
print("\n[3] Analyzing card details and pricing...")
analyzed_cards = []
for i, card in enumerate(cards[:5], 1): # Analyze first 5 results
card_name = card.get("name", "Unknown")
card_edition = card.get("edition", "Unknown")
card_url = card.get("url", "")
print(f"\n Card {i}: {card_name} ({card_edition})")
if card_url:
try:
# Get detailed pricing information
details = client.get_card_detail(url=card_url)
conditions = details.get("data", {}).get("conditions", [])
# Find best deal
best_deal = find_best_deal_across_conditions(details.get("data", {}))
if best_deal:
condition = best_deal.get("condition", "N/A")
price = best_deal.get("price", "N/A")
available = best_deal.get("available", "0")
print(f" Best price: {price} ({condition}) - {available} available")
analyzed_cards.append({
"name": card_name,
"edition": card_edition,
"url": card_url,
"best_price": price,
"condition": condition,
"available": available
})
else:
print(f" No stock available at any condition")
except Exception as e:
print(f" Error fetching details: {str(e)}")
# Step 4: Check buylist prices for cards we analyzed
print("\n[4] Checking buylist prices for 'Mox' cards...")
buylist_result = client.get_buylist_prices(query="Mox", page=1)
buylist_items = buylist_result.get("data", {}).get("items", [])
print(f"Found {len(buylist_items)} cards in buylist (showing first 5):")
for item in buylist_items[:5]:
name = item.get("name", "Unknown")
edition = item.get("edition", "Unknown")
prices = item.get("buylist_prices", {})
cash_price = prices.get("cash", "N/A")
credit_price = prices.get("credit", "N/A")
max_qty = prices.get("max_quantity", "0")
print(f"\n {name} ({edition})")
print(f" Cash: {cash_price} | Store Credit: {credit_price} (Max qty: {max_qty})")
# Check for arbitrage opportunities from our analyzed cards
for analyzed in analyzed_cards:
if analyzed["name"].lower() == name.lower():
margin = calculate_arbitrage_opportunity(analyzed["best_price"], credit_price)
print(f" → Arbitrage margin (retail to credit): {margin:.1f}%")
# Step 5: Check current deals to find bargains
print("\n[5] Checking current deals and discounts...")
deals_result = client.get_deals(page=1)
deals = deals_result.get("data", {}).get("items", [])
print(f"Found {len(deals)} items on sale (showing first 5):")
for deal in deals[:5]:
name = deal.get("name", "Unknown")
original = deal.get("original_price", "0")
sale = deal.get("sale_price", "0")
try:
original_float = float(original.replace("$", ""))
sale_float = float(sale.replace("$", ""))
savings = original_float - sale_float
discount_pct = (savings / original_float) * 100
print(f"\n {name}")
print(f" ${original} → ${sale} (Save ${savings:.2f}, {discount_pct:.1f}% off)")
except (ValueError, AttributeError):
print(f"\n {name}: {original} → {sale}")
# Step 6: List sample editions for reference
print("\n[6] Sample of available editions for filtering...")
editions_data = client.list_all_editions()
editions = editions_data.get("data", {}).get("editions", [])
print(f"Total editions in catalog: {len(editions)}")
print("Sample editions (first 6):")
for edition in editions[:6]:
print(f" - {edition['name']} ({edition['slug']})")
# Step 7: Summary and insights
print("\n" + "=" * 70)
print("WORKFLOW SUMMARY")
print("=" * 70)
print(f"✓ Analyzed {len(analyzed_cards)} specific card listings")
print(f"✓ Reviewed {len(buylist_items)} buylist opportunities")
print(f"✓ Found {len(deals)} current deals")
print(f"✓ Indexed {len(editions)} available editions")
if analyzed_cards:
print(f"\nTop opportunities to track:")
for card in analyzed_cards[:3]:
print(f" • {card['name']} ({card['edition']}): {card['best_price']}")
print("\nWorkflow complete! Ready to use for real-time MTG market analysis.")
print("=" * 70)Search for Magic: The Gathering cards by name, edition, and format. Returns paginated results with pricing and condition availability.
| Param | Type | Description |
|---|---|---|
| page | integer | Page number for pagination |
| query | string | Search keyword for card name (e.g. 'Lightning Bolt') |
| format | string | Filter by tournament format: Commander, Legacy, Modern, Pauper, Vintage, Pioneer, Brawl, Standard, Premodern |
| edition | string | Filter by card set/edition name |
{
"type": "object",
"fields": {
"page": "integer, current page number",
"items": "array of card objects with name, url, edition, rarity, type, oracle_text, and conditions (price/availability per condition)",
"per_page": "integer, results per page"
},
"sample": {
"data": {
"page": 1,
"items": [
{
"url": "https://www.cardkingdom.com/mtg/beta/lightning-bolt",
"name": "Lightning Bolt",
"type": "Instant",
"rarity": "C",
"edition": "Beta",
"conditions": [
{
"price": "$449.99",
"available": "0",
"condition": "NM"
},
{
"price": "$359.99",
"available": "3",
"condition": "EX"
}
],
"oracle_text": "Lightning Bolt deals 3 damage to any target.",
"collector_number": "162"
}
],
"per_page": 100
},
"status": "success"
}
}About the Card Kingdom API
Card Search and Detail
The search_cards endpoint accepts a query string for card name lookup, an edition filter for set-specific results, and a format filter supporting nine tournament formats including Commander, Modern, Legacy, Pioneer, and Standard. Results are paginated via the page parameter and each item includes the card's name, edition, rarity, type, oracle_text, and a conditions array containing price and availability per grading condition. For deeper data, get_card_detail takes a card url path and returns the full conditions list with quantities, the card's oracle text, and an other_versions array linking to alternate printings of the same card.
Buylist and Deals
The get_buylist_prices endpoint returns what Card Kingdom will pay for cards — each item in the response includes buylist_prices with distinct cash, credit, and max_quantity fields. You can filter by query or edition and paginate results. The get_deals endpoint surfaces currently discounted products with original_price, sale_price, and a direct url to the listing, making it straightforward to track markdown events across the catalog.
Editions and Formats Reference
list_all_editions returns the full MTG set catalog as an array of objects with name, slug, and url — useful for building edition pickers or validating set names before passing them to search_cards or get_buylist_prices. get_formats returns a keyed object of all supported game formats with each format's id, name, slug, and is_active status, giving you the canonical format identifiers Card Kingdom recognizes.
The Card Kingdom API is a managed, monitored endpoint for cardkingdom.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when cardkingdom.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official cardkingdom.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Compare retail prices across card conditions to find the lowest-cost copy of a specific printing
- Track buylist cash vs. store credit rates to identify the best time to sell cards back
- Monitor the deals feed for price drops on specific cards or products
- Build a format-legality price checker using the format filter in search_cards
- Generate a full set inventory by iterating search_cards with an edition parameter from list_all_editions
- Alert on buylist max_quantity limits to detect when Card Kingdom stops buying a given card
- Cross-reference other_versions from get_card_detail to find cheaper alternate printings of a card
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does Card Kingdom have an official developer API?+
What does the buylist endpoint actually return — and how does it differ from retail pricing?+
get_buylist_prices endpoint returns what Card Kingdom will pay when you sell cards to them, not what they charge buyers. Each item includes a buylist_prices object with separate cash and credit values and a max_quantity field indicating how many copies they are currently buying. Retail sell prices come from search_cards or get_card_detail via the conditions array.Does the API cover foil or special-treatment card variants separately?+
conditions arrays in search_cards and get_card_detail reflect the condition grades and quantities Card Kingdom lists. Foil and non-foil versions of a card typically appear as separate listings, accessible via get_card_detail with the relevant url or through the other_versions field. Explicit foil flags are not a dedicated response field currently. You can fork the API on Parse and revise it to add a foil-specific filter or field if that distinction matters to your use case.Is historical price data available through any of the endpoints?+
How does pagination work across endpoints that support it?+
search_cards, get_buylist_prices, and get_deals all accept an integer page parameter. Each response includes the current page number and a per_page count so you can calculate total pages from the result set size. list_all_editions and get_formats return their full datasets in a single response with no pagination.