Understanding Unmet Demand
Unmet demand is one of the silent revenue leaks in shared mobility: a rider opens the app, looks for a vehicle, and there isn't one nearby. They close the app and don't take a ride. You never see the missed transaction in your rides table because the ride never happened — but the demand was real, and AI Ops now captures it.
What counts as an unmet-demand event
Every time a rider opens the Levy mobile app and the in-zone vehicle list returns zero rideable vehicles within 500 meters of their search location, AI Ops records an event in the unmet_demand_events table. The event includes:
| Field | Description |
|---|---|
h3_index | The H3 hex the search happened in |
occurred_at | Timestamp of the search |
available_vehicles_500m | Count of vehicles within 500m (always 0 for unmet events) |
search_lat / search_lng | Where the rider was searching |
customer_uuid | The rider, when authenticated |
The instrumentation lives in POST /api/mobile/app-session-search — the rider mobile app calls it on every map-open and vehicle-list query.
Why we capture it
Two reasons:
- Quantify lost revenue. If you know a hex has 30 unmet-demand events per day and your average fare is $4, that's about $120 of potential daily revenue from that hex alone. The rebalance recommender uses this to dollarize its suggestions.
- Correct the forecast for censoring. Historical rides are a censored signal of demand — you only see the rides that happened, not the ones that would have happened if a vehicle had been there. The forecast model uses unmet-demand events to reconstruct true demand, so it doesn't underestimate hexes that have been chronically under-supplied.
The unmet-demand heatmap layer
On Dashboard > Analytics > Heat Maps, toggle the Unmet demand layer. Hexes are colored by the count of unmet-demand events in the visible window (default: last 24 hours).
Bright red hexes are where you're leaking the most revenue. They're the highest-priority destinations for the rebalance recommender — moving vehicles there has the highest projected lift.
Click any hex to see:
- Count of unmet events in the window
- Time-of-day distribution (when riders are searching)
- Forecast for the next 4 hours (predicted demand on the same hex)
Comparing unmet demand to forecast
Unmet demand is observed history. Forecast is prediction. They're complementary:
- High unmet, high forecast — chronic under-supply. The recommender will surface this first.
- High unmet, low forecast — short-lived surge (e.g. a one-time event). The recommender treats this with caution because the forecast horizon doesn't predict it.
- Low unmet, high forecast — you've been keeping up. Forecast says you'll need to keep doing so.
- Low unmet, low forecast — quiet hex, leave it alone.
Retention
Unmet-demand events are stored in a TimescaleDB hypertable with a 30-day retention policy. Older rows are dropped automatically. Aggregates roll up into the forecast feature pipeline before they age out, so the model retains the signal even after individual events are deleted.
Privacy
Search locations (search_lat / search_lng) are personal data. They are covered by the existing rider Terms of Service and are stored only for the 30-day retention window. They are never included in MDS feeds or any third-party export.
Related
- Demand forecast map — full guide to the Heat Maps page.
- Rebalance recommendations — how unmet demand becomes a suggested move.