Alimenta: ML data pipeline and search polish

New Year's Day deep dive on Alimenta. Started with data infrastructure, ended with UI polish.

The data pipeline

Built a four-phase pipeline to collect Seattle restaurant data from Google Places API:

  1. Fetch Place IDs — Text search across 46 grid zones covering Seattle. Uses the free tier (100k requests/month). Output: ~3,000 unique Place IDs.

  2. Fetch Details — Hit the Enterprise tier for full restaurant data. $20 per 1,000 requests. This is where the cost lives.

  3. Process Data — Extract ML features: cuisine type, neighborhood, price level, service model, competition density, whether they have a full bar, website presence.

  4. Train Model — Random Forest Regressor with cross-validation. Predicts expected rating based on structural features. Gem score = actual rating minus predicted.

The grid search uses variable density—tighter spacing in downtown, Capitol Hill, and U District where restaurants cluster. Looser in residential neighborhoods. Total cost for 2,355 restaurants: about $30.

Model results

The updated dataset jumped from 1,205 to 2,355 restaurants. Discoveries more than doubled: 117 to 299.

Distribution:

  • 💎 Rare Find (≥+0.5): ~60 restaurants
  • ✨ Gem (+0.3 to +0.49): ~120 restaurants
  • 📍 Notable (+0.2 to +0.29): ~120 restaurants

About 12.7% of restaurants qualify as discoveries. That feels right—selective enough to mean something, common enough to be useful.

Security hardening

Moved the photo API from URL-based to header-based authentication. Photo references in query strings felt wrong. Also added strict validation for Place IDs (must match Google's format) and sanitized error responses.

Added an on-demand details endpoint. UX fields like hours, reservable, and delivery aren't needed for the map view—fetching them for 2,355 restaurants upfront would cost $47. Instead, we fetch when someone opens a restaurant card. Saves money, data stays fresh.

The badge problem

The tier badge was competing with restaurant names for attention. In dark mode it was barely visible. In light mode it felt like a sticker slapped onto the card.

Moved the tier info into the rating row as a parenthetical: ⭐ 4.8 (✨ +0.4). Reads more naturally—the tier modifies the rating rather than standing alone.

Also increased card padding throughout. Small changes that compound into something less cluttered.

Navigation fixes

Selecting a restaurant from search used to clear your results. Now the search state persists—close the restaurant card and you're back at your list.

Added a close button to the desktop card (mobile had swipe-to-dismiss, desktop had nothing). Added search history that shows your last five queries when focusing an empty search bar.

Fixed the fullscreen photo gallery. All photos were visible at once on desktop. The fix: overflow-hidden. Sometimes the bug is just a missing CSS property.