The problem

I didn't need a commercial product, I needed a tool for family use: no accounts, no subscriptions, everything local. The goal was simple to state and less simple to build — type a product name once and see the best price across four different platforms, each with its own structure, its own filters, its own affiliate links.

The most direct technical solution would have been a scraper for each site. The more robust one — and the one I chose — was to delegate the search to a model with real-time web access.

A two-speed architecture

The backend is FastAPI on Python, the frontend a single-page HTML that talks to localhost:8000. The interesting part is how the search is split:

TaskHow it works
AmazonA dedicated, separate Gemini 2.0 Flash task (_search_amazon_via_ai), ready to switch to the official PA-API v5 as soon as it's available
AliExpress / eBay / TrovaprezziA single Gemini 2.0 Flash call with Google Search grounding (_search_others_via_gemini)
Parallelismasyncio.gather launches the two groups in separate executors
PersistenceLocal SQLite (bivio.db), auto-created on first run

The detail that cost me the most time: Amazon has to stay out of the "others" group even when the official PA-API is disabled. I tried it, initially, to simplify the code — result: Gemini returned an empty array for Amazon every time it was bundled together with the other three. Two separate tasks, two separate prompts, problem solved.

The affiliate link trick

A problem I didn't expect: the product links Gemini returns in its response are often made up — plausible-looking URLs that lead to 404 pages or to different products. Google Search grounding helps find the right results, but it doesn't guarantee the final link is real.

The solution was to stop trusting the links generated by the model and rebuild them myself, server-side, with a single function (_affiliate_url) that generates real search URLs with tracking already included:

  • Amazon: amazon.it/s?k={query}&tag=...
  • AliExpress: aliexpress.com/wholesale?SearchText={query}&tblci=... (not tracking_id, which looks correct but isn't)
  • eBay: query string with campid and mkevt from the eBay Partner Network
  • Trovaprezzi: direct search link, no active affiliation

Gemini's job is to decide what to search for and estimate indicative prices. The links the user actually clicks are always generated by my own code, never copied from the model's response.

Wishlist, price alerts and a silent scheduler

On top of the search runs a second layer, built for daily use rather than for a demo: a wishlist to save interesting products, and an alert system that notifies you when a price drops below a set threshold. APScheduler checks all alerts every 24 hours in the background, plus a manual endpoint (/api/alerts/check) to force a check when you don't want to wait.

From home to anywhere

BIVIO runs entirely locally — no server to pay for, no data leaving the house. To use it outside the house too, I added a Cloudflare Tunnel: no open port on the router, no static IP to configure, just cloudflared tunnel run bivio pointed at the domain.

The real problems

Three concrete obstacles, the kind you don't find in the official docs.

1. The antivirus was intercepting HTTPS calls. Requests to Gemini failed with intermittent SSL errors. Cause: the local antivirus was doing TLS inspection on outgoing connections. Fix (temporary, not ideal): a global patch with ssl._create_unverified_context applied to httpx.

2. The Gemini quota runs out, and at an embarrassing moment. To record a demo reel, the daily quota was already gone. Structural solution: two API keys in cascade (GEMINI_API_KEY_2 primary, GEMINI_API_KEY as fallback), and for live demos, data injected directly after the backend response instead of waiting for the quota reset.

3. Amazon's PA-API requires 3 qualifying sales before it activates. Associates credentials alone are only enough to generate the tracking tag, not to query the official API — you need a sales history. In the meantime, Amazon goes through the same Gemini + grounding channel as the other three marketplaces, ready to switch to PA-API v5 the day the sales history unlocks it.

The AI model doesn't replace the integration with official APIs — it anticipates it. The day PA-API unlocks, I change one function and the rest of the architecture stays identical.

Actual cost

  • Hosting: €0/month (runs on a home PC)
  • Gemini 2.0 Flash: free on the free tier, with a dual fallback key
  • Cloudflare Tunnel: free
  • SQLite: included, zero configuration

What I'd add

The official Amazon PA-API as soon as the required 3 sales mature, a price history to understand whether it's worth buying now or waiting, and a mobile-first version of the frontend — currently built mostly for a home desktop.