A command-line tool for searching Magic: The Gathering cards on Cardmarket (EU) with advanced filtering and shipping cost calculations.
- 🔍 Search for MTG cards on Cardmarket
- 📦 Export Data Mode - Uses daily Cardmarket export files by default (no API calls needed!)
- 💰 Compare prices including shipping costs to your location
- 🎁 Per-Booster Pricing - Automatic price-per-booster calculations for sealed products (boxes, bundles, prerelease packs)
- 📈 Expected Value (EV) Calculator - Calculate expected value of booster boxes using real card prices from Scryfall
- 🌍 Filter by seller country and shipping availability
- ⚡ Dual data sources: Fast export data for basic searches, live API for advanced features
- 📊 Display results in table or JSON format
- 🎯 Advanced filtering (condition, foil, language, set, price range, product type)
- 🔄 Automatic daily data updates with manual refresh option
- 📦 Group offers by seller to optimize shipping
- 🎨 Customizable display (hide foil column, show/hide per-booster pricing)
- 🔍 Product type filtering (singles only, sealed products only, or both)
- Node.js >= 18.0.0
- pnpm package manager
- Cardmarket API credentials (optional - only required for live API mode with
--liveflag or advanced features)
- Clone the repository:
git clone <repository-url>
cd cardmarket-tools- Install dependencies:
pnpm install- Create a configuration file:
cp config.example.json config.json- Edit
config.jsonwith your preferences. API credentials are optional - only needed for live API mode.
API credentials are only required if you want to use live API features (real-time seller offers, condition filtering, shipping calculations).
For basic price lookups using export data (default mode), you can skip this section entirely and remove the credentials section from your config.json.
To get API credentials for live mode:
- Log in to your Cardmarket account
- Visit: https://www.cardmarket.com/en/Magic/Account/API
- Create a new "Dedicated App" or "Widget" application
- You'll receive:
- App Token (Consumer Key)
- App Secret (Consumer Secret)
- Access Token
- Access Token Secret
For more details, see:
Create a config.json file in the root directory:
{
"credentials": {
"appToken": "your-app-token",
"appSecret": "your-app-secret",
"accessToken": "your-access-token",
"accessTokenSecret": "your-access-token-secret"
},
"preferences": {
"country": "DE",
"currency": "EUR",
"language": "en",
"maxResults": 20,
"defaultSort": "avg",
"hideFoil": true,
"showPerBooster": true,
"productFilter": "both"
},
"cache": {
"enabled": true,
"ttl": 3600
},
"export": {
"enabled": true,
"autoUpdate": true
},
"ev": {
"enabled": true,
"autoUpdate": true,
"updateFrequency": "weekly",
"bulkCardThreshold": 1.0,
"showVariance": false,
"confidenceThreshold": 0.7
}
}credentials: Your Cardmarket API credentials (optional - only required for live API mode with--liveflag or advanced features like--condition,--include-shipping, etc.)preferences.country: Your country code for shipping calculations (ISO 3166-1 alpha-2)preferences.currency: Preferred currency display (EUR, USD, GBP, etc.)preferences.language: Interface languagepreferences.maxResults: Default maximum number of results (default: 20)preferences.defaultSort: Export data sort order - options:trend,low,avg,name,none(default:avg)preferences.hideFoil: Hide foil price column in export results (default: true)preferences.showPerBooster: Show per-booster price column for sealed products (default: true)preferences.productFilter: Filter products by type - options:singles,nonsingles,both(default:both)cache.enabled: Enable/disable response caching (default: true)cache.ttl: Cache time-to-live in seconds (default: 3600)export.enabled: Enable export data mode (default: true)export.autoUpdate: Automatically download export data if missing or old (default: true)ev.enabled: Enable Expected Value calculations (default: true)ev.autoUpdate: Automatically download Scryfall data if missing or old (default: true)ev.updateFrequency: How often to update Scryfall data - options:daily,weekly,manual(default:weekly)ev.bulkCardThreshold: Minimum card price to include in EV calculations in EUR (default: 1.0)ev.showVariance: Show variance range in EV results (default: false)ev.confidenceThreshold: Minimum confidence for EV calculations (default: 0.7)
This tool supports two data sources:
- What it is: Daily snapshots of all MTG products and price trends from Cardmarket
- Includes: Singles AND sealed products (booster boxes, prerelease packs, bundles, etc.)
- Size: ~41MB total (18MB singles + ~23MB sealed products + 23MB price guide)
- Update frequency: Daily (automated download on first run, manual updates available)
- Storage: Local
./datadirectory (gitignored) - Default sorting: By average price (ascending) - configurable in config.json
- Advantages:
- ⚡ Extremely fast searches (no API calls)
- 🆓 No API rate limits
- 📊 Includes price trends and statistics
- 🎁 Includes sealed products (boosters, boxes, packs)
- 📈 Smart sorting (cheapest options first)
- Limitations:
- No individual seller offers
- No condition/foil/signed filtering
- No real-time shipping calculations
- Data refreshed daily (may be slightly outdated)
- What it is: Real-time queries to Cardmarket's API
- Advantages:
- 🔴 Live seller offers with real-time availability
- 🎯 Full filtering (condition, foil, signed, altered)
- 📦 Real shipping cost estimates
- ⚡ Most up-to-date prices
- Limitations:
- Requires API credentials
- Subject to rate limits (30,000 requests/day)
- Slower than export mode
The tool automatically chooses the best data source:
Uses Export Data when:
- Basic card searches (name only)
- Price range filtering
- No shipping calculations needed
- Export mode is enabled (default)
Switches to Live API when:
--include-shippingflag is used- Condition filtering (
--condition NM) - Special card attributes (
--foil,--signed,--altered) - Country filtering (
--filter-country) - Seller grouping (
--group-by-seller) --liveflag is explicitly set- Export mode is disabled
pnpm run buildBasic search (uses export data):
pnpm start search "Black Lotus"Update export data:
# Update Cardmarket export data if old (>24 hours)
pnpm start update-data
# Update Scryfall EV data if old (>7 days)
pnpm start update-data --scryfall-only
# Update both Cardmarket and Scryfall data
pnpm start update-data --forceForce live API mode:
# Get real-time seller offers
pnpm start search "Black Lotus" --live
# Live mode with condition filtering
pnpm start search "Mox Pearl" --live --condition NMSearch with filters:
# Search for Near Mint cards (switches to API automatically)
pnpm start search "Lightning Bolt" --condition NM
# Search for foil cards in English (uses API)
pnpm start search "Tarmogoyf" --foil --language EN
# Search with price range (uses export data)
pnpm start search "Mox Pearl" --min-price 100 --max-price 500
# Search for specific set
pnpm start search "Force of Will" --set ALLSealed products with per-booster pricing and EV:
# Find booster boxes with per-booster cost and expected value
pnpm start search "Bloomburrow Play Booster Box" --show-ev
# Calculate EV for multiple sets
pnpm start search "booster box" --show-ev --top 10
# Search only sealed products (no singles)
pnpm start search "Bloomburrow" --product-filter nonsingles --top 10
# Search only singles (no sealed products)
pnpm start search "Lightning Bolt" --product-filter singles
# Show foil prices too (overrides hideFoil preference)
pnpm start search "Foundations Bundle" --show-foil
# Hide per-booster column (overrides showPerBooster preference)
pnpm start search "Duskmourn Collector Booster Box" --hide-per-boosterInclude shipping costs:
# Show total price including shipping
pnpm start search "Volcanic Island" --include-shipping
# Only show sellers who ship to your country
pnpm start search "Underground Sea" --include-shipping --filter-country
# Show top 10 offers by total cost
pnpm start search "Tundra" --include-shipping --top 10Output formats:
# Table format (default)
pnpm start search "Birds of Paradise"
# JSON format
pnpm start search "Birds of Paradise" --json
# Save to file
pnpm start search "Birds of Paradise" --json > results.jsonAdvanced options:
# Group by seller to optimize shipping
pnpm start search "Sol Ring" --include-shipping --group-by-seller
# Sort results
pnpm start search "Counterspell" --sort price
pnpm start search "Dark Ritual" --sort seller-rating
# Disable cache for fresh results
pnpm start search "Brainstorm" --no-cache| Option | Description | Values | Default |
|---|---|---|---|
--condition |
Card condition (forces API mode) | NM, EX, GD, LP, PL, PO | - |
--foil |
Only foil cards (forces API mode) | boolean | false |
--signed |
Only signed cards (forces API mode) | boolean | false |
--altered |
Only altered cards (forces API mode) | boolean | false |
--language |
Card language | EN, DE, FR, IT, ES, JP, etc. | - |
--set |
Expansion set code | 3-letter set codes | - |
--min-price |
Minimum price | number | - |
--max-price |
Maximum price | number | - |
--include-shipping |
Include shipping costs (forces API mode) | boolean | false |
--filter-country |
Filter sellers by shipping to your country | boolean | false |
--top |
Show top N offers | number | - |
--group-by-seller |
Group offers by seller | boolean | false |
--sort |
Sort results | price, condition, seller-rating | - |
--json |
Output in JSON format | boolean | false |
--live |
Force live API mode instead of export data | boolean | false |
--no-cache |
Disable caching for this request | boolean | false |
--max-results |
Maximum results to show | number | from config |
--show-foil |
Show foil price column (overrides hideFoil) | boolean | from config |
--hide-per-booster |
Hide per-booster column (overrides showPerBooster) | boolean | from config |
--product-filter |
Filter by product type | singles, nonsingles, both | both |
--show-ev |
Show expected value for sealed products | boolean | false |
pnpm start help
pnpm start --help
pnpm start search --helpFind the cheapest Near Mint Black Lotus including shipping:
pnpm start search "Black Lotus" --condition NM --include-shipping --sort price --top 5Find foil Lightning Bolts in English under 10 EUR:
pnpm start search "Lightning Bolt" --foil --language EN --max-price 10Compare booster box values by expected value:
pnpm start search "Foundations Play Booster Box" --show-ev
# Output includes EV data:
# Foundations Play Booster Box | €124.88 avg | €3.47 per booster | Box EV: €799.54 | Ratio: 6.40x | ✓ PosFind the best EV booster boxes:
pnpm start search "booster box" --show-ev --top 10 --product-filter nonsingles
# Compares EV ratios across different setsExport search results to JSON:
pnpm start search "Mana Crypt" --json > mana-crypt-prices.jsoncardmarket-cli/
├── src/
│ ├── index.ts # Main CLI entry point
│ ├── config.ts # Configuration loader
│ ├── api/
│ │ ├── client.ts # API client with OAuth
│ │ ├── auth.ts # OAuth signature generation
│ │ └── endpoints.ts # API endpoint methods
│ ├── export/
│ │ ├── types.ts # Export data TypeScript types
│ │ ├── downloader.ts # Export file downloader
│ │ └── searcher.ts # Export data searcher
│ ├── ev/
│ │ ├── types.ts # EV TypeScript types
│ │ ├── scryfall-downloader.ts # Scryfall bulk data downloader
│ │ ├── scryfall-loader.ts # Card data loader with indexing
│ │ ├── expansion-mapper.ts # Maps Cardmarket to Scryfall sets
│ │ ├── collation-engine.ts # Booster pack composition engine
│ │ └── ev-calculator.ts # Main EV calculation logic
│ ├── commands/
│ │ ├── search.ts # Search command implementation
│ │ ├── help.ts # Help command
│ │ └── types.ts # Shared TypeScript types
│ └── utils/
│ ├── shipping.ts # Shipping cost calculations
│ ├── formatter.ts # Output formatting
│ ├── cache.ts # Caching utilities
│ └── booster-count.ts # Per-booster pricing lookup
├── data/ # Data files (gitignored)
│ ├── products_singles.json # Singles export data
│ ├── products_nonsingles.json # Sealed products export data
│ ├── price_guide.json # Price guide export data
│ ├── booster-counts.json # Booster count database
│ ├── scryfall_cards.json # Scryfall card data with EUR prices
│ ├── scryfall_metadata.json # Scryfall data metadata
│ ├── expansion_mapping.json # Cardmarket to Scryfall mapping
│ └── booster_collation.json # Pack composition rules
├── config.example.json # Example configuration
├── package.json
├── tsconfig.json
└── README.md
Watch mode for development:
pnpm run watchClean build artifacts:
pnpm run cleanCardmarket API has rate limits:
- Standard accounts: 30,000 requests/day
- Professional sellers: 100,000 requests/day
This tool includes built-in caching to minimize API calls. Cache is enabled by default with a 1-hour TTL.
- API Documentation - Detailed Cardmarket API reference
- Future Features - Planned enhancements
- Cardmarket API 2.0 Documentation
- Authentication Guide
- OAuth Header Format
- Products Endpoint
- Articles Endpoint
"Failed to load export data" error:
- Run
pnpm start update-datato download export files - Check internet connection (files are ~41MB total)
- Verify
./datadirectory is writable
"Export data is more than 24 hours old" warning:
- Run
pnpm start update-datato refresh data - Or use
--liveflag to bypass export data
EV calculation showing "N/A":
- Run
pnpm start update-data --scryfall-onlyto download Scryfall data - Ensure product is a sealed booster product (not singles)
- Check that the set is supported (recent sets preferred)
"Invalid OAuth signature" error:
- Verify your API credentials in
config.json - Ensure your system clock is synchronized (OAuth uses timestamps)
- Note: Only needed for live API mode, not export mode
"Rate limit exceeded" error:
- Use export data mode (default) to avoid rate limits
- Enable caching (default)
- Reduce the number of requests
- Wait for the rate limit to reset (daily)
No results found:
- Check card name spelling
- Try without filters first
- Some cards may not be available in the specified condition/language
- Export data includes singles and sealed products, but not all product types
MIT License - See LICENSE file for details
Contributions are welcome! Please feel free to submit issues or pull requests.
This tool is not affiliated with or endorsed by Cardmarket. Use responsibly and in accordance with Cardmarket's Terms of Service and API usage policies.