A command-line tool that helps Magic: The Gathering players in Canada find the cheapest prices for missing cards across multiple online stores (currently supports FaceToFaceGames, TopDeckHero, TopDeckBoucherville, TopDeckJoliette, and MTGJeuxJubes, with more stores coming soon).
You provide a simple text list of cards you need, and the tool searches each store, normalizes results, compares prices, and outputs an Excel spreadsheet with the best available deals.
- Scrapes multiple Canadian MTG stores for card prices (FaceToFaceGames, TopDeckHero, TopDeckBoucherville, TopDeckJoliette, and MTGJeuxJubes)
- Automatic caching β search results are cached for 24 hours to improve performance
- Multi-page support β scrapes up to 2 pages of results per store for better coverage
- Modular architecture β add new stores easily
- Multiple selection strategies β choose cheapest, best condition, foil/non-foil preferences
- Quality filtering β set minimum quality requirements to avoid buying cards in poor condition
- Uses consistent data models for clean comparison
- Outputs a neatly formatted Excel file with all details
- Fast and efficient β uses store APIs when available
- Designed for CLI use β lightweight and scriptable
- Clone this repository:
git clone https://github.com/rm2631/Price.Finder.git
cd Price.Finder- Install dependencies:
pip install -r requirements.txtLaunch the web interface:
streamlit run streamlit_app.pyYour browser will automatically open to the MTG Deal Finder interface. Simply paste your card list and click "Search for Cards"!
The easiest way to use MTG Deal Finder is through the web interface:
streamlit run streamlit_app.pyThis will open a browser window where you can:
- Paste your card list directly (no file needed)
- Configure all search options through an intuitive interface
- View results in an interactive table
- Download results as Excel directly in your browser
All CLI options are available in the UI with helpful tooltips explaining each option.
Create a text file with one card per line (e.g., cards.txt):
Lightning Bolt
Sol Ring
Counterspell (7ED)
Brainstorm x4
Run the tool:
python -m mtg_deal_finder cards.txt --out results.xlsxpython -m mtg_deal_finder <input_file> [options]
Options:
--out, -o FILE Output Excel file path (default: results.xlsx)
--store STORES Comma-separated list of stores to search (e.g., facetoface,topdeckhero,topdeckboucherville,topdeckjoliette,mtgjeuxjubes)
--strategy, -s STRATEGY Selection strategy for choosing best card (default: cheapest)
--min-quality, -q QUAL Minimum card quality/condition to consider (e.g., nm, lp, mp)
--topdeck-discount Apply TopDeck's 20% checkout discount to prices (applies to TopDeckHero, TopDeckBoucherville, TopDeckJoliette, and MTGJeuxJubes)
--no-cache Disable caching of search results
--debug Enable debug loggingYou can filter results to only show cards that meet a minimum quality requirement:
# Only show Near Mint or better cards
python -m mtg_deal_finder cards.txt --min-quality nm
# Only show Lightly Played or better cards
python -m mtg_deal_finder cards.txt --min-quality lpQuality levels (from best to worst):
- mint or m: Mint condition
- nm: Near Mint
- lp: Lightly Played
- mp: Moderately Played
- played or pl: Played
- hp: Heavily Played
- damaged or dmg: Damaged
When you specify a minimum quality, only cards at that quality level or better will be considered. For example, --min-quality lp will show Near Mint and Lightly Played cards, but filter out Moderately Played, Played, Heavily Played, and Damaged cards.
The tool supports multiple strategies for selecting the best card offer:
- cheapest (default): Selects the lowest price regardless of condition or foil status
- cheapest-nonfoil: Selects the cheapest non-foil version
- cheapest-foil: Selects the cheapest foil version
- foil-first-cheapest: Prefers foil cards - selects the cheapest foil if available, otherwise falls back to the cheapest non-foil
- foil-within-50cents: Selects foil if the price difference between the cheapest foil and cheapest non-foil is within 50 cents, otherwise selects non-foil
- foil-within-1dollar: Selects foil if the price difference between the cheapest foil and cheapest non-foil is within $1, otherwise selects non-foil
- best-condition: Selects the cheapest Near Mint condition card
- blingiest: Selects the most expensive foil card (for "bling" factor)
All strategies respect the minimum quality filter if specified.
Example using a strategy:
python -m mtg_deal_finder cards.txt --strategy best-condition --out nm_results.xlsxExample combining strategy with minimum quality:
# Get the cheapest non-foil card, but only LP or better
python -m mtg_deal_finder cards.txt --strategy cheapest-nonfoil --min-quality lpTopDeck stores (TopDeckHero, TopDeckBoucherville, and TopDeckJoliette) offer a 20% discount at checkout. You can apply this discount to TopDeck prices using the --topdeck-discount flag:
# Apply the 20% discount to TopDeck prices
python -m mtg_deal_finder cards.txt --topdeck-discountWhen this flag is enabled, all TopDeck store prices (stores with "topdeck" in their domain) will be reduced by 20% before comparison, giving you a more accurate view of the final price you'll pay.
Example:
- Without discount: TopDeck price is $10.00
- With discount: TopDeck price is $8.00 (20% off)
This makes it easier to compare TopDeck prices with other stores that don't offer a checkout discount.
You can limit your search to specific stores:
# Search only TopDeckBoucherville
python -m mtg_deal_finder cards.txt --store topdeckboucherville
# Search only FaceToFaceGames
python -m mtg_deal_finder cards.txt --store facetoface
# Search multiple specific stores
python -m mtg_deal_finder cards.txt --store facetoface,topdeckhero,mtgjeuxjubes
# Search all stores (default)
python -m mtg_deal_finder cards.txtSearch results are automatically cached for 24 hours to improve performance on repeated searches. Cached results older than 24 hours are automatically deleted when a new search is performed, ensuring you always get fresh pricing data. To disable caching:
python -m mtg_deal_finder cards.txt --no-cacheThe input file supports various formats:
- Simple card name:
Lightning Bolt - Card with set:
Counterspell (7ED)(set code is parsed but not used for filtering) - Card with quantity:
Brainstorm x4or4x Brainstorm - Combined:
Counterspell (7ED) x2 - Moxfield format:
1 Choked Estuary (MIC) 169(leading quantity, set code, and collector number) - With card number:
1 Aragorn and Arwen, Wed (LTR) 394(card number 394 is parsed but not used for filtering) - Comments: Lines starting with
#are treated as comments and ignored
Note on Set Information and Card Numbers:
While the tool supports parsing set codes (e.g., (7ED)) and card numbers (e.g., 394) from various input formats, these are always ignored during searches. The tool will find the cheapest available printing of the card regardless of set or collector number. This ensures you get the best price across all available versions of the card.
The tool generates an Excel file with the following columns:
| Selected | Query | Card | Set | Condition | Foil | Price | Quantity | Store | URL |
|---|---|---|---|---|---|---|---|---|---|
| β | Lightning Bolt | Lightning Bolt | Masters 25 | Played | False | $1.99 | 1 | FaceToFaceGames | https://... |
| Lightning Bolt | Lightning Bolt | Premium Deck | Near Mint | False | $2.49 | 1 | FaceToFaceGames | https://... | |
| Lightning Bolt | Lightning Bolt | The List | Played | False | $1.99 | 0 | FaceToFaceGames | https://... |
Key features:
- All offers are included - The Excel file contains every available offer found across all stores, not just the selected ones
- Selected offers are marked - The "Selected" column shows a β for offers chosen by your selection strategy
- Quantity indicates availability - 1 means in stock, 0 means out of stock
- Sorted for easy review - Results are sorted by card name, selected offers first, then by price
This allows you to review all available options and understand why a particular offer was selected, making it easy to choose an alternative if needed.
The tool accepts a plain text file or multiline string where each line represents a card.
Example input:
Lightning Bolt
Sol Ring
Counterspell (7ED)
Brainstorm x4
Each line is parsed into a structured Card object with attributes: name, set, and quantity.
Folder structure:
mtg_deal_finder/
βββ main.py # CLI entrypoint and orchestration
βββ cards.py # Card and Offer data models
βββ strategies.py # Selection strategies for choosing best offers
βββ stores/
β βββ base.py # Abstract StoreScraper interface
β βββ facetoface.py # FaceToFace scraper (uses store API)
β βββ topdeckhero.py # TopDeckHero scraper (HTML scraping)
β βββ [future stores] # Additional store implementations
βββ compare.py # Price aggregation and comparison logic
βββ output.py # Excel export logic
βββ utils/
βββ caching.py # Local caching layer (24-hour TTL)
βββ normalization.py # Fuzzy matching and validation
- Card β represents a card with fields:
name,set(optional),qty(default 1) - Offer β represents a store offer with:
store,card,set,condition,price,url,foil, andavailability
Each store implements the same interface with a search(card) method returning a list of offers.
This makes adding new stores straightforward.
- API-first approach: Use store APIs when available (e.g., FaceToFaceGames search API)
- Static HTML fallback: Use requests + BeautifulSoup for stores without APIs (e.g., TopDeckHero)
- Dynamic pages: Use Playwright or Selenium in headless mode when necessary (reserved for future stores)
- Caching: Cache results locally (24-hour TTL) to avoid re-fetching identical pages
- Pagination: Scrape up to 2 pages per store for better coverage
- Rate limiting: Add delays between page requests to stay polite and avoid rate limits
The tool supports multiple strategies for selecting the best offer from available options:
- CheapestStrategy: Select the lowest price regardless of condition or foil status
- CheapestFoilStrategy: Select the cheapest foil version
- CheapestNonFoilStrategy: Select the cheapest non-foil version
- FoilFirstCheapestStrategy: Prefer foil cards - select the cheapest foil if available, otherwise the cheapest non-foil
- FoilWithin50CentsStrategy: Select foil if the price difference is within 50 cents of the cheapest non-foil
- FoilWithin1DollarStrategy: Select foil if the price difference is within $1 of the cheapest non-foil
- BestConditionStrategy: Select the cheapest Near Mint condition card
- BlingiestStrategy: Select the most expensive foil card
Each strategy implements a select(offers) method that returns the best offer according to its criteria. This design makes it easy to add new strategies in the future (e.g., "cheapest within a specific set", "best condition within budget", etc.).
Ensure consistent formatting of card names, sets, and conditions:
- Strip extra spaces, normalize capitalization and punctuation
- Optionally validate via Scryfall API for canonical names and set codes
- Handle foil and non-foil variants consistently
- Filter out non-English cards
Aggregate all offers from multiple stores, then use the selected strategy to choose the best offer for each card. This yields a clean, minimal list of best options across all stores.
Results are exported to Excel using pandas with columns:
| Card | Set | Condition | Foil | Price | Store | URL |
|---|---|---|---|---|---|---|
| Lightning Bolt | Masters 25 | Near Mint | False | $2.49 | FaceToFaceGames | https://... |
The file is automatically sorted by price (cheapest first) and includes clickable URLs to product pages.
- Language: Python 3.11+
- Core libraries: requests, beautifulsoup4, pandas, openpyxl
- Optional: playwright, fuzzywuzzy, scrython
- Create a text file (e.g.,
missing.txt) with one card per line - Run:
python -m mtg_deal_finder missing.txt --out deals.xlsx - Open the resulting Excel file to view the best prices
Keep it simple, modular, and extensible:
- Add new stores by subclassing the
StoreScraperclass - Add new selection strategies by implementing the
SelectionStrategyinterface - Swap scraping backends easily
- Focus on accurate data normalization and comparison
- Add more stores (FusionGaming, 401Games, etc.)
- Implement multithreaded scraping for faster results
- Include shipping cost estimation
- Integrate login/cart linking
- Save results in a local SQLite database
- Check each site's Terms of Service before scraping
- Use caching and delays to reduce server load
- This project is intended for personal use only
MIT License β free for personal use, modification, and sharing.
Built by an MTG player, for MTG players.