personal project · 2026

CardScan

Pokémon Card Market Scanner

i collect Pokémon cards and wanted a better way to investigate asking prices before buying. CardScan compares listings with completed sales, makes the cost assumptions explicit and shows how a bounded 30-day trend estimate affects the result.

PythonFastAPIReactSQLiteAPI IntegrationData Analysis
see what i built
01 / Market OverviewSample Data shown
CardScan dashboard with 108 sample listings and 18 saved cards, showing the first eight results with asking prices, market estimates, trends and profit.
The demo compares 108 listings across 18 different cards/condition combinations. The dataset includes NM, LP, MP and HP conditions, with separate generated sales for each condition. All prices shown are sample data, not measured returns.

the problem

A card can look underpriced until shipping, selling fees or a falling market erase the margin. Comparisons also become misleading when listings mix different variants, languages, conditions or grades.

CardScan brings those checks into one workflow, with the evidence and assumptions beside each estimate.

what i built

01 - React + Vite

Research workspace

Responsibility: Presents listings, filters, watchlists, charts and an itemised resale calculation in one browser interface.

How it works: The interface requests a selected dataset from the API, applies the user's filters and opens each listing beside its comparable sales and editable cost assumptions.

02 - FastAPI

Application API

Responsibility: Serves the frontend, validates requests and coordinates searches, recalculation, imports, exports and watchlist changes.

How it works: API routes validate input, call the relevant data or valuation service and return structured results; state-changing routes additionally require the operator session and CSRF checks.

03 - Python

Market data model

Responsibility: Normalises cards, listings and completed sales around set, number, language, variant, condition and grading.

How it works: Imported or provider records are converted into one exact identity schema before any listing is allowed to use a group of completed sales as evidence.

04 - Statistical analysis

Valuation engine

Responsibility: Filters unusual sales, weights recent evidence and calculates current value, trend support and an inspectable cost breakdown.

How it works: Exact completed sales pass through outlier filtering and recency weighting, then an evidence-gated trend projection and the user's fees and shipping assumptions produce the displayed estimate.

05 - SQLite

Persistence layer

Responsibility: Stores cards, sales, listings, scan targets and the watchlist in a portable local database.

How it works: Validated snapshots are written in one transaction, watchlist identities remain stable and a timestamped database backup is created before an imported dataset is replaced.

06 - Connector interface

Provider adapters

Responsibility: Keeps active listing and completed sale providers behind a shared, testable boundary.

How it works: An on-demand scan runs bounded target requests through each configured adapter, validates the response and publishes a new live snapshot only after the scan succeeds.

02 / Inside The EstimateSample Listing
Umbreon VMAX sample listing with historical sale prices, a dashed 30-day projection and an itemised resale calculation.
A listing opens into its comparable sales and cost breakdown, so the estimate can be inspected rather than taken on trust.

engineering decisions

Exact identity matching

I match records on set, card number, language, variant, condition and grading because visually similar marketplace titles can refer to materially different cards. The same card could be listed in many different ways, so this allows us to filter the card we want.

Conservative resale profit

Estimated resale starts at 80% of the lower of current market value and the bounded projection, then subtracts both shipping costs and selling fees. This can understate upside, so every cost assumption remains editable and visible beside the result.

Isolated data modes

Live scans, sample data and imported snapshots are kept separate so demonstration records cannot appear to be current market evidence. The extra mode handling adds interface complexity, but preserves the provenance of every estimate.

Atomic snapshots over partial imports

Imports are fully validated and backed up before cards, sales and listings are replaced together. Rejecting an entire invalid snapshot is less flexible than accepting good rows individually, but prevents the database from entering a mixed or internally inconsistent state.

current outcome

108

illustrative demo listings

18

exact card identities in the portfolio demo

Tests cover pricing, validation, persistence and mocked provider requests. Browser checks cover search, filters, watchlist saving, cost recalculation and CSV export.

Live market and profit estimates require authorised completed-sale data. Without it, those fields stay blank. The trend projection has not been heavily backtested on real market data, so it remains supporting evidence rather than a promised future price.

The app does not buy cards, schedule scans or send alerts. Grading, returns and holding costs are not modelled.

what's nextlast edited ·

  • Extend to Facebook Marketplace.

    date TBC
  • Extend live valuation beyond near mint cards.

    date TBC
  • Backtest the valuation and trend estimates against real historical outcomes.

    date TBC
  • Consider broader identity matching and additional feeds after the core data path is validated.

    date TBC