Back to case studies

Retro Price

901,713 sold-price records across 3,753 titles by the Mar 2026 snapshot; main app now offline

For physical-media collectors (VHS, DVD, Blu-ray, LaserDisc, 4K UHD) who need real sold comps, not asking prices, to decide when to buy or sell.

Retro Price site screenshot

At a glance

Role
Creator and technical lead (solo)
Timeframe
Aug 2025 (data collection start) – Mar 2026 snapshot
Team
Solo (AI-assisted with Claude Code)
Users
No user numbers published
Status
Main app offline; standalone build showcase live (Mar 2026 snapshot)
Stack
PHP 8.x, Python 3.12, PostgreSQL (JSONB), Redis, scikit-learn, Alpine.js

Problem and stakes

Physical-media prices are volatile and hard to read. Asking prices mislead, graded copies sell for many times more than ungraded ones, and community buzz moves prices before listings show it. Collectors need comps based on actual sold data.

Constraints

The eBay API allows 5,000 calls a day, shared across every operation. Live API calls made search take 5–10 seconds. Reddit title extraction was noisy: in early runs, 68% of extracted titles were false positives.

Options considered

Live API search vs. a database-first architecture: live calls were too slow, so search moved to precomputed local data.

Decision

Go database-first. Ingest sold listings on a schedule, store raw payloads as JSONB, and serve search and price guides from PostgreSQL aggregates. Score market "waves" from scarcity, Reddit momentum, and seasonality, and separate graded from ungraded items so premiums don't skew the statistics.

Program plan

Phase 1: PostgreSQL schema, eBay OAuth integration, TMDB/OMDb metadata. Phase 2: Reddit trending, graded-item detection, wave analysis. Phase 3: database-backed search, Alpine.js frontend, price guide. Phase 4: production hardening (SSL, Nginx headers, rate-limiter fixes, OPcache, daily pipeline).

Technical system

PHP handles the web app and API integrations, and Python runs ML and heavy analysis. A cron pipeline runs twice daily in five steps: TMDB-driven eBay crawl across all formats, Reddit trending scan (16 collector subreddits), AI price analysis, wave analyzer, and ML predictions.

Wave score (0–100) = scarcity 40% + Reddit 40% + seasonality 20%, mapped to Peak, Heating up, Stable, Cooling, and Bottom. A weekly scikit-learn retrain on 7-day price change is blended with the rules.

Graded items (VGA, WATA, CGC, PSA, factory sealed) are detected by PostgreSQL regex functions. Other sources: Heritage Auctions, Goodwill deal polling, and Amazon new releases. Public v1 read APIs use keys and rate limits.

Execution

Moving search to the database cut response times from 5–10 seconds of live API calls to about 150–200 ms. Reddit false positives were reduced with a blacklist, proper-noun and length checks, and confidence levels. An infinite "Searching..." spinner was traced to four causes: Alpine.js not loaded, over-strict validation, a rate limiter returning the wrong type, and unnormalized format strings.

Outcome

Production snapshot (Mar 2026, from the live showcase): 901,713 price records, 3,753 titles with comps, 3,770 catalog titles, and 38,075 wave analyses. By format: DVD 376,539 listings, Blu-ray 293,176, 4K UHD 154,007, LaserDisc 73,453, VHS 4,538. The data also surfaced surprise valuations such as Akira (1988) on VHS averaging $1,543.99 sold. The main app is now offline, and the showcase is the standing artifact.

Reflection

Database-first beats API-first for data people look up often. JSONB adds flexibility without schema churn. Rate limits push you toward creative caching. Documenting during development is easier than reconstructing it afterward.

Proof links