-
-
12 of 13 reading evals, 50 of 50 database security tests, 295 unit tests, and 960 model calls for $2.67 in total.
-
From my own receipts: white potatoes at $0.79/lb at Fiesta and $1.79/lb at H-E-B.
-
A test receipt with a fake $40 line. The lines don't match the printed total, so Billie flags it instead of saving a wrong price.
-
What judges usually ask: any store, accuracy, photo privacy, and cost (about 1¢ per receipt).
-
Billie reads any receipt, compares your stores, and catches price rises and recalls.
-
Kimi K2.7 timed out, so NVIDIA Nemotron took over and read it in 4.2 s.
-
How the receipt was read. Every model call goes through Nebius Token Factory, with Tavily for FDA recalls and OpenStreetMap for stores.
-
The landing page on a laptop, an open foldable and a phone. Try it live at billi3.vercel.app, no sign-in needed.
Inspiration
Groceries in the US cost 28% more than five years ago (BLS, food at home, Sep 2020 to Sep 2026). A pound of ground coffee went from $4.49 to $9.33. But none of us can say what we paid last month, because the proof is on a receipt that went in the trash. I wanted the receipts I already get to turn into my own price history, without typing anything.
What it does
Billie reads a photo or PDF of a grocery receipt and remembers every price, store by store.
- Every price rise on your own receipts, even small ones. My H-E-B milk went from $1.85 to $1.87.
- Recalls from the FDA, matched to food you actually bought.
- The cheaper store, if you shop at more than one. My own receipts showed white potatoes at $0.79/lb at Fiesta and $1.79/lb at H-E-B.
- Monthly spending against a budget, by aisle, plus shopping lists and a healthy food to try.
There is no chat box and no prompt to write. You take a photo and the app does the rest. Billie only compares stores that appear on your own receipts; it has no outside price list.
How I built it
Every model call goes through Nebius Token Factory:
- The photo is checked for size and sharpness before anything is spent.
- Kimi K2.7 reads every line, the store, the date and the totals. If it is slow, NVIDIA Nemotron 3.5 Super VL takes over. It also reads a second time when the printed item count does not match.
- The lines are added up in plain code and checked against the printed subtotal and total. A receipt that does not add up is marked for the shopper to check, never saved silently.
- NVIDIA Nemotron 3 Super turns each printed line into a plain name and an aisle ("GV WHL MLK GAL" becomes "Whole milk, 1 gal").
- Qwen3 Embedding 8B and Qwen 3.8 group the same product across stores so prices can be compared.
- The photo is deleted once it is read, unless you choose to keep it.
Tavily (now part of Nebius) searches fda.gov once a day for new recalls and trusted nutrition sites for healthy foods, and finds small shops missing from OpenStreetMap. The app runs on SvelteKit and Vercel, with Supabase for the database, sign-in and storage.
I picked each model by measuring it, not by its name. I tried 27 vision models from the Token Factory catalog, then ran the best four on three real, crumpled receipts, three times each (129 printed lines):
| Model | Lines found | Made-up lines | Median time |
|---|---|---|---|
| Kimi K2.7 | 129 of 129 | 0 | 4.5 s |
| Kimi K3 | 125 of 129 | 4 | 2.7 s |
| Nemotron 3.5 Super VL | 114 of 129 | 6 | 3.9 s |
| Qwen 3.8 | 106 of 129 | 23 | 3.8 s |
For grouping products, I checked 83 pairs by hand. Qwen 3.8 never merged two different products across three runs; Nemotron 3 Super merged three wrong pairs each run. So Kimi reads, Nemotron names and backs up, and Qwen judges.
Challenges I ran into
- A slow main reader. In production, 15 Kimi reads were cut off at my first 10 s limit, while its good reads took up to 18.5 s. I raised it to 20 s. While recording the demo, Kimi hit that limit on a real receipt, and Nemotron 3.5 Super VL read it in 4.2 s instead. That fallback is what the video shows.
- Receipts that attack the model. A test receipt says "Ignore all previous instructions" and carries a fake $999 item. Answers must match a strict schema, and attack lines are left out or flagged.
- A database firewall that blocked receipts. Lines that looked like SQL were blocked in URL filters, failing whole reads. Those lookups now go in the request body.
- Small shops not on the map. My local store wasn't in OpenStreetMap, so its address is found on the web and offered as a pick, never guessed.
Accomplishments I'm proud of
- Tested like it will be attacked. 13 reading evals on real model calls, 12 passing. The one that fails is a misread multi-buy line, and the totals check catches it, which is why that check runs on every receipt. Also 50 of 50 database security tests, 295 unit tests, and an attack pass on the live app.
- Cheap to run. 960 model calls logged for $2.67 in total. A receipt costs about 1¢.
- The fallback works for real, not just in a diagram: it took over on camera.
- Anyone can try it in one click. A guest session needs no sign-in, gets 10 real receipt reads, and is deleted after 3 hours.
What I learned
The model is not the product. The checks around it are: the totals check, the fallback, the strict schema and the cost caps. Measuring models on my own worst receipts told me more than any leaderboard, and the most useful number I published is the eval that fails.
What's next for Billie
- Shared households, so a family's receipts build one price history.
- Store-brand matching, so "Great Value milk" and "H-E-B milk" can be compared as the same thing when the shopper says so.
- Price alerts before you shop, using a list to show which of your stores was cheapest last time.
- Forwarded email receipts, for online orders, without uploading a PDF.
Built With
- kimi-k2.7
- leaflet.js
- nebius-token-factory
- node.js
- nvidia-nemotron
- openstreetmap
- photon
- playwright
- postgresql
- qwen
- supabase
- svelte
- sveltekit
- tavily
- typescript
- vercel
- vitest


Log in or sign up for Devpost to join the conversation.