This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
A $5 bassinet at a thrift store can be a recalled product that's illegal to resell, and nothing on it tells you.
That's the problem I am trying to solve. Selling a recalled product is illegal in the U.S. even secondhand and even if you're one person flipping stuff on Facebook Marketplace on weekends. A lot of what gets recalled is exactly the stuff that piles up at thrift stores like cribs, bassinets, sleepers, high chairs, strollers, toys.
I pulled every CPSC recall since 2015 to see how bad it is. About 1 in 5 is a kids’ product, and more than half of those (55%) don’t list a model number or UPC at all in their product data. The only way to know is what the thing looks like.
Every recall tool I found wants text. A brand, a model number, a SKU from your store catalog. A reseller's haul has none of that. No box, no SKU, half the time you don't even know the brand.
So I built ClearToList. You take a photo of the item, it finds the closest recalls. Then you take a photo of the label and it settles it by model number or UPC. You get one of three answers, shown as a price-dot sticker on your own photo:
- 🟥 Recalled: a model number or UPC on the item matches a recall. Don't list it.
- 🟨 Check the label: it looks like a recalled product. That alone means nothing, so it asks for a label photo.
- ⬜ No match found: nothing matched. This never means "safe". There's no green sticker on purpose.
Only Recalled is an alarm. My first version also alerted when something just looked like a recalled product. Turns out a lot of normal white cribs look like recalled white cribs and that evaluation talked me out of it.
Buyers get it too. Looking at a Marketplace listing? Screenshot it and check before you pay. If it looks like a recalled product, the page gives you a message to send the seller asking for a label photo. Resellers can check a whole haul at once (up to 10 photos) and get one report as CSV or PDF.
Who pays for this
Nobody yet, and nothing is charged today. My guess at the model:
- Free for buyers and casual checks. Parents shouldn't pay to find out if a crib is recalled.
- Resellers pay a monthly fee for bigger hauls, alerts when something they already checked gets recalled later, and a dated "checked" record per item. That record is the thing you want if a buyer ever comes back at you.
- Consignment and kids' resale stores pay per location. They take in used baby gear from strangers every day and carry the most risk.
- Marketplace licensing is the long shot. A check built into the listing flow of a resale app.
Demo
Real results from the live site, I just cut the waiting. Demo photos: Wikimedia Commons (ajay_suresh, Sally V, Hardyplants, קרלוס הגדול), CC BY 2.0 / CC BY-SA / CC0.
Try it yourself: cleartolist.onrender.com
No sign-up. Three sample photos run real scans, and everyone gets free photo checks every day. Want to use it regularly? Drop your email for an invite code and I'll send one within a day. Model number, UPC and brand lookup is always open.

Take a photo, pick one from your library, or try a sample. The label is step 2.

The label's model number matches a recall. Red sticker, what to do whether you're selling or buying, and the recall itself.

Looking similar is never an alarm. Yellow sticker, the closest recalls to compare, and a button to add the label. (Play yard photo: Famartin, CC BY-SA 4.0, via Wikimedia Commons.)
Code
Check secondhand products against U.S. product recalls before you list them, children's products first.
Resellers buy unboxed, unbranded secondhand items with no SKU, so catalog-based recall tools can't help them. And selling a recalled consumer product is illegal no matter how small the seller. ClearToList works in two photos: a photo of the item finds the closest CPSC recalls, and a photo of its label settles it by model number or UPC. You get one of three verdicts:
| Verdict | Meaning |
|---|---|
| 🟥 CONFIRMED | A model number or UPC read off the item matches a recalled product. Don't list it. |
| 🟧 POSSIBLE MATCH | The item looks similar to the recalls shown side by side with your photo. That alone doesn't mean it's recalled: photograph the label and scan it. |
| ⬜ NO MATCH FOUND | No match in CPSC recalls. That does not mean the item is safe. |
Only CONFIRMED is an alarm…
How I Built It
Data
All CPSC recalls since 2015 come from the CPSC Recalls API: about 3,400 in scope and around 12.7k product photos. The ingest only pulls what changed, backs off politely and falls back to a cache if the API is down. A GitHub Actions job refreshes it every night and redeploys the site's data. Car seats are out because NHTSA handles those, not CPSC.
The scan
flowchart LR
P[Item photo] --> A[SigLIP 2<br/>image search over<br/>~12.7k recall photos]
P --> G[Gemma 4 reads the photo<br/>JSON: item type, brand,<br/>text, model no., UPC]
G --> E[Exact match<br/>model no. / UPC]
G --> T[EmbeddingGemma<br/>text search]
A --> F{Weighted rank fusion}
T --> F
E -->|boost to top| F
F --> S[Top 3 shortlist]
S --> V{Rule}
V -->|code matches| C[Recalled]
V -->|closest photo very similar| M[Check the label<br/>show shortlist,<br/>ask for label photo]
V -->|otherwise| N[No match found]
L[Label photo] --> G2[Gemma 4 reads<br/>model no. / UPC] --> E2[Exact match] --> C
- Gemma 4 E4B reads the photo and returns a JSON: item type, brand, any visible text, model number, UPC.
- Search. SigLIP 2 searches the recall photos, model numbers and UPCs are matched exactly, brands are matched, and EmbeddingGemma searches the recall text. Weighted reciprocal rank fusion combines it all. For a normal product photo the image leads. Text only leads when the photo is basically a label and Gemma actually read words off it.
- A dumb rule decides, not a model. Code match means Recalled, unless the brand on the item contradicts the recall. Very similar closest photo means Check the label. Otherwise, No match found. I wanted the answer to come from something I can read in ten lines and not from a model non-deterministically.
Picking the reader
I tried Gemma 4 E2B and E4B on 30 held-out photos. First prompt was bad because Gemma kept saying "baby product" instead of "bassinet". Asking for a specific item type got both to 15/30, and E4B was faster (3.6 s vs 6.2 s median), so E4B it is. A full scan is about 7s.
One gotcha is SigLIP on the GPU left too little room for Gemma and EmbeddingGemma to stay loaded, so Ollama swapped them on every single scan and added 25s. So, I had to move SigLIP to the CPU and that solved the problem and it's still well under a second there.
The site and not getting wrecked by a public upload form
The site runs on Render's free tier and runs no models itself. Photo checks go into a small in-memory queue, and a separate GPU worker running the open models connects out to the site over signed HTTPS to pick up jobs. It accepts no incoming connections. It sends back only recall ids and matched codes, and Render re-checks any code against its own copy of the data before it ever shows Recalled.
Security
- Uploads are checked by their actual bytes, capped at 8 MB and ~30 megapixels, and re-encoded, which strips GPS and anything hidden in the file.
- Photos live in memory only and get deleted right after the check.
- Per-person rate limits, a daily cap on free checks, a small queue that says no when it's full, and a kill switch for uploads.
- Worker requests are HMAC-signed with a timestamp and nonce.
- Anything Gemma reads off a label is treated as untrusted. It can't set the verdict, and the page sits behind a strict Content Security Policy.
- No cookies, no IPs, no tracking. Just anonymous daily counts and a "Was this helpful?" button.
Evaluation
Every test query is a held-out recall photo roughed up to look like a phone shot (crop, rotation, dim light, blur), and that photo plus every near-duplicate of it is hidden from the index. The only way to score is to find other photos of the same recall. For negatives I used licensed Wikimedia photos of normal, non-recalled products in the same categories. Rules were picked on a dev split and the test ran once.
There are three different numbers here and they're easy to mix up, so here's what each one means for someone holding the item:
| From one item photo | Kids' products | Other categories | Label close-ups |
|---|---|---|---|
| Flagged "Check the label" with the right recall | 34% | 37% | 15% |
| Right recall shown at all (incl. grey "closest recalls to check") | 63% | 38% | 57% |
| Right recall somewhere in the internal top 3 (not shown to you) | 67% | 64% | 60% |
| Recalled verdicts (right / total) | 0 / 0 | 2 / 3 | 2 / 2 |
| False Recalled alarms on normal lookalikes | 0% | 0% | n/a |
The top row is the one that matters: from a photo alone, ClearToList points you at the right recall about a third of the time. The 67% in the third row only says the search is good enough to build on. I hide most of that shortlist on purpose, because showing it every time flagged almost every normal product.
The grey suggestions in row two raise it to 63% for kids' products, but 98% of normal lookalikes get suggestions too. That's why they're grey and never called a match.
The label photo is what makes it reliable. Recalled needs a model number or UPC off the item. Of the five Recalled verdicts in the test, four were correct. None of the 124 tested lookalikes triggered a Recalled verdict. These are encouraging early results, but the sample is too small to establish real-world reliability. Cribs and sleepers only get 45% top 3 from the image, because one white crib looks a lot like another. On label close-ups, Gemma reading the label finds the recall where image search can't (60% vs 18%).
I also set a kill criterion before building the app: about 60% internal top-3 on kids' products, or I drop the idea. The spike hit 71% and the test landed at 67-69%. That number answers "is the search good enough to build on", not "what does a user see". The user number is the 34%.
It took three runs to get these numbers. Run 1 had a Gemma "verifier" that was right about half the time and raised alarms on 31% of normal kids' items, so I cut it. The full run history is in the Full evaluation.
Why Does Open Innovation Matter?
- The math only works if a scan is basically free. A reseller might check hundreds of items a week. On open models a scan costs a fraction of one API call, and there's no per-call bill to scale with usage. That's the difference between "free for parents" being a real promise or a trial.
- Photos of people’s stuff stay private. People photograph things in their homes, sometimes with their kids' stuff around. Those photos go to my worker, get checked and get deleted. They never get sent to a third-party model API.
- I could actually tune it. Forcing strict JSON out of Gemma, swapping E2B for E4B, moving SigLIP to the CPU, picking thresholds on a dev split: all of that is versioned in the repo, so nothing shifts under the evaluation because a vendor updated a model. And the whole pipeline can be fine-tuned on recall photos later.
My First User
It went live this weekend, so I don't have a real user story yet. I'll update this section with actual numbers and whatever people tell me.
If you flip on eBay, Poshmark or Marketplace or you're buying used baby gear, run one item through it and tell me in the comments what it got right or wrong.
Prize Categories
- Gemma: Gemma 4 reads the item and its label, which is the core of the whole thing. EmbeddingGemma powers the text search.
-
Render: the hosted site and job queue, deployed from
render.yaml. - GitHub Copilot (GitHub Actions): Actions is what keeps ClearToList current without me touching it. A scheduled workflow runs every night at 09:17 UTC: it pulls only the recalls CPSC added or changed since the last run, rebuilds the site’s SQLite database (full-text search plus exact model-number and UPC tables), publishes it as a data-latest release asset and calls Render’s deploy hook, so the live site picks up a new recall the next morning. A second workflow runs ruff, the full pytest suite and a gitleaks scan over the whole git history on every push. During launch weekend a third one pings the site every 10 minutes so the free instance doesn’t fall asleep on a visitor.
What's next
- Per-category thresholds to cut down noise outside kids' products (sports gear and kitchen stuff are the worst)
- More recall sources: EU Safety Gate (it has photos) and Health Canada
- Real thrift store photos in the eval
- More scanning capacity by running the same worker on rented GPUs
- Alerts that re-check your past items when a new recall drops
This is a screening aid only, not legal advice. Recall data is from the U.S. Consumer Product Safety Commission. Not affiliated with or endorsed by CPSC.
Top comments (0)