A Django app built to help a client resell a 16,000-piece miniatures collection on eBay. It identifies a figure from the number stamped on its base, matches it against a 1,140-figure reference catalog I built, and turns the result into a ready-to-post listing. Built to make a slow, expert-knowledge resale workflow fast and repeatable.
A client has a collection of roughly 16,000 pre-painted D&D and Pathfinder miniatures to sell on eBay. Listing one by hand means photographing it, identifying which set it came from and its figure number, researching a fair price against completed sales, writing a description, and filling out the eBay form: about 10 to 15 minutes each. Across the whole collection that is on the order of 3,200 hours of work, with no system tracking status, condition, or pricing.
A full-stack Django app with a mobile-friendly capture flow. Photograph a figure, read the number off its base to find it in the reference catalog, confirm the match, then build and post the eBay listing without leaving the app.
Every figure has a number stamped on its base, like "23/60". The app reads that number and looks it up in the catalog, using the set total (the "/60") to narrow what would otherwise be dozens of matches down to the right set. It is fast, deterministic, and free, which beat image recognition on every count for this job.
The lookup only works because of the data behind it: roughly 1,140 known figures, assembled by scraping public set checklists (minisgallery, miniscollector, Wikipedia) and reconciling them against each other. A separate scraper backfills a reference photo for each entry so matches can be confirmed by eye.
Single figures or multi-item bundles. The app auto-generates an eBay-compliant title (capped at 80 characters) and a description from the catalog data, and offers a one-click "completed sales" search to set a fair price. Everything stays editable before posting.
Listings post straight to eBay through the Inventory API using OAuth, with a sandbox/live toggle for safe testing, daily API-usage tracking, and seller policy IDs stored in the database so credentials can be updated without redeploying.
Where does AI come in? It is an optional fallback, toggleable in settings, for the occasional figure whose base label is worn or missing. When it is off, those go straight to manual entry. The deliberate decision here was that a catalog I controlled was more accurate, cheaper, and more reliable than leaning on image recognition for the common case.
Each physical figure moves through a clear status flow (draft, needs review, manual entry, ready) before it can be added to a listing, so the collection is always in a known state.
Items that need attention before they can be listed. Ready items move to New Listing.
| Item | Set | Condition | Status | Action | ||
|---|---|---|---|---|---|---|
| Beholder Lich | Unhallowed #31 | Near Mint | Ready | |||
![]() |
Young Green Dragon | Dragon Collector's Set #3 | Near Mint | Manual Entry | Review | |
| Frost Giant | Tyranny of Dragons #27 | Near Mint | Ready | |||
| Faceless Stalker | Rise of the Runelords #6 | Near Mint | Ready | |||
| Roper | Underdark #56 | Near Mint | Ready |
Dragon Collector's Set · #3
Reproduced from the app's own templates and styling, using real items from the database. The Young Green Dragon's base label reads 3 / 5: that number is the catalog key the lookup turns into a full identification.