Refreshing the Travel-Time Map Behind Lyft’s Marketplace: Rebuilding Neighborhood Reachability Signals
Every time Lyft calculates pricing to balance a market, nudges a driver toward an under-served pocket of a city, or paints a heatmap of where demand is building, there is a quiet lookup table doing work in the background. It answers a deceptively simple question: how long does it take to get from here to there?, for millions of pairs of places, across hundreds of regions.
That lookup table is the Neighborhood Reachability Signal, and for years large parts of it were frozen in a snapshot of the world from 2018–2019. This is the story of how we rebuilt it, why a refresh substantial enough to be worth adopting was what finally moved Pricing to switch, the cleanly positive results that came out of that switch, and where we’re taking it next, from one static file per region to time-aware travel times that change with the rhythm of the day.
What is a Neighborhood Reachability Signal?
A geohash is a compact way of carving the world into a grid of cells. At geohash-6 resolution, each cell is roughly the size of a few city blocks. Slice a region into geohash-6 cells and you get a clean, discrete coordinate system for “neighborhoods” that downstream systems can reason about.
The Forecasting & Real-Time Optimization (FORTOP) team produces the Neighborhood Reachability Signals dataset, which consists of two companion files for each region:
- Neighborhood Reachability Matrix: the estimated travel time, in minutes, between the centers of pairs of geohash-6 cells. Think of it as a sparse origin-to-destination travel-time matrix for a region.
- Neighborhood Centers: the list of all geohashes that appear in the ETA files for that region, i.e. the “vocabulary” of cells that the marketplace is allowed to talk about.
Both files are generated offline on a schedule by an Airflow DAG. They are static in the sense that they are precomputed and shipped, rather than queried live (which is exactly what makes them cheap to read at high frequency in latency-sensitive systems).
Who relies on it
The Geohash ETA dataset is one of those pieces of infrastructure whose customer list is longer than you’d expect, because much of the dependency is indirect.
The most direct consumers are:
- Dynamic Pricing & Offer Selection: Lyft’s real-time pricing engine, internally “Graph” uses Neighborhood Reachability Signals to decide which demand–supply geohash pairs are even worth modeling and how important each potential match is. A lower ETA between a rider’s geohash and a driver’s geohash means a better matching opportunity, and therefore a stronger pull on the price. In other words, the ETA dataset literally defines the structure of the graph that pricing optimizes over.
- Real-Time Supply / Driver Bonus Heatmaps: The real-time supply team uses the same files to reason about where supply can reach demand, and to render the heatmaps that tell drivers where it’s worth going.
- Neighborhood features: The FORTOP team uses the dataset to publish a neighborhood version of our real-time features, rolling up each geohash together with its reachable neighbors,which a much wider set of teams then build on. If a model anywhere in the marketplace reasons about a geohash and its reachable neighbors, there’s a good chance a Neighborhood Reachability Signal is somewhere upstream.
Curious how FORTOP produces these real-time features in the first place? Take a look at our companion posts, Real-Time Spatial-Temporal Forecasting @ Lyft and Evolution of Streaming Pipelines in Lyft’s Marketplace.
A dataset frozen in 2019
The version of the dataset running in production had its core ETAs last meaningfully refreshed around 2018–2019. That sounds alarming, but the reason it stayed in place is more mundane than it looks: the refreshes that happened in between were incremental.
Early on, we introduced versioning for the ETA files, which gave us a clean way to iterate on the dataset and to run experiments against whatever version was in production. Over the years the files went through several such iterations — coverage was extended and formats changed. None of these, though, represented a large enough change to the underlying ETAs to create a forcing function for adoption by a consumer like Pricing. Swapping the dataset that a system like Graph optimizes over is not a free action: it means designing and running experiments, validating that the marketplace behaves better, and absorbing the risk of a regression. When a new version is only marginally different from the one already in production, that cost is hard to justify and the expected upside is small. Plans to move forward did exist and had real momentum, but without a clearly substantial change to point to, they never quite materialized into a launch.
There was also a hidden tax in the meantime. Because the source dataset had gaps — missing geohash pairs that were genuinely matchable, and cells that shouldn’t have been included at all — consumers had to compensate downstream. Teams maintained their own denylists to strip out bad or non-drivable geohashes, and their own allowlists to add back geohashes the source had dropped. In effect, every consumer was patching the same dataset in parallel to work around the same gaps.
Two deficiencies in the legacy data were behind much of this:
- It under-estimated travel times. Its ETAs were systematically lower than reality, which made Graph believe drivers were closer than they actually were.
- It was missing pairs that were genuinely matchable. Because of how the geohash universe was built, many demand–supply pairs inside the matching radius simply weren’t in the dataset. If a pair wasn’t present, the system behaved as if no driver could be there at all — even when one was physically close enough to match.
In the second half of 2025, FORTOP shipped a redesigned version that produced more accurate and more complete ETAs and added new fields so teams could shape the data without maintaining their own patches. Paired with validation of how it improved on the legacy data, this was the refresh that Pricing adopted.
How the files are built
Before diving into what changed, it helps to understand the pipeline’s shape. We’ll keep this at the altitude of a flight map rather than a wiring diagram.
At a high level, the Airflow DAG produces each region’s files in a handful of conceptual stages:
- Decide the geohash universe. Start from a maintained list of candidate geohashes for the region, then narrow it down using real demand and supply history — primarily where riders have actually had sessions and where drivers have actually been.
- Form the pairs. Take that set of geohashes and build the candidate origin–destination pairs that need a travel-time estimate.
- Estimate the travel times two ways. For pairs with enough observed history, the ETA is derived from historical trip data. For pairs that are too sparse to trust (fewer than a handful of observations), we fall back to the Routing Simulation System (RSS), which produces a simulated ETA that also factors in traffic conditions to keep the estimate realistic. Pairs with partial history get a weighted blend of the two.
- Emit the two files. Write out the Neighborhood Reachability Matrix file and the Neighborhood Centers file for downstream teams to consume.
The elegance of this design is that it degrades gracefully: well-traveled corridors lean on real history, and the long tail of rarely-traveled pairs still gets a reasonable estimate from simulation rather than a hole in the matrix.
What changed in the redesign
The redesign was anchored by a very visible gap. Coincidentally, around this time the issue was also flagged publicly — the real-time map was surfacing demand in places no car could go, such as cells sitting over open water in the SFO region — and it drew a reply from Lyft CEO David Risher. The root cause traced back to the geohash universe itself. The pipeline never checked whether a geohash was actually drivable before including it and expanding to its neighbors. It was a concrete instance of exactly the kind of gap consumers had been patching by hand.
1. Drivable geohashes as a source of truth.
Working with the Mapping team, we brought in a set of drivable geohashes derived from the road network itself (Lyft’s directional-segment map data). Rather than just sampling road segments at their endpoints, we interpolate many points along each segment, which dramatically improves coverage near region boundaries and on long road segments and bridges — exactly the places naive approaches tend to drop. These drivable geohashes are used in two ways: as a positive signal when building the geohash universe, and as a final denylist that strips out any cell sitting over water or other non-navigable terrain. The map-over-water bug simply disappears.
Removal is only half the story. In dense regions the net effect is trimming, but in sparser markets, many tier-2 regions and parts of the Midwest, for example, the refreshed workflow often adds geohashes, filling in coverage the old pruning had dropped.
2. A gentler pruning strategy.
The legacy pipeline aggressively trimmed geohashes to keep the dataset small, which was a big contributor to those “missing but matchable” pairs. The redesign reworks this:
- Established regions (more than a year of history) now retain every geohash that has seen at least one rider session in the past year, instead of dropping the bottom slice by session count.
- New and expanding regions (less than a year of history) skip session-based pruning entirely and start from the full set of drivable geohashes, so a young market isn’t penalized for not yet having a long history. These regions are automatically promoted to the “established” path once they cross the one-year mark, and the whole distinction is made dynamically inside the DAG. There’s a trade-off here, though: with no session history to attach signals to, consumers can’t use that metadata to pare down what they read for these regions. We’re evaluating exposing additional metadata derived from whatever history a region has accumulated so far, so that even not-so-new regions get a lever to limit the data they load.
This was a deliberate trade: more coverage in exchange for more pairs to process. It directly attacks the “phantom missing supply” problem that had been quietly distorting pricing.
3. New fields for consumers.
The redesign surfaces additional fields so that downstream teams can shape the data without us having to fork the pipeline for each of them. For example, a field that aggregates rider-session density around each destination geohash (within an 8-minute radius) lets a consumer bound how much of the file it needs to read, and the drivable-geohash set itself is written alongside the output. Together, these additional attributes let consumers do their own pruning and filtering on top of the dataset — exactly the self-service that removes the need for hand-maintained denylists and allowlists.
This brief clip demonstrates how consumer systems utilize rider-session density to prune their specific geohash environments. Note how coverage builds dynamically, radiating outward from the highest-density hubs to the quietest corners of the region.
How Pricing adopted it — and what they saw
Because the ETA dataset defines the very structure Graph optimizes over, swapping it out is not a quiet change — it reshapes the marketplace’s entire view of feasible supply. So Pricing treated it like a first-class experiment, running the refreshed dataset against the legacy one as a two-week time-split test across a set of top pricing regions.
The most illuminating result is structural. Looking at each geohash’s feasible neighbors (the cells reachable within a 20-minute ETA), the shape of “nearby supply” shifted substantially:
Two forces drive this. First, over-optimism gets corrected: pairs of the old data labeled as “≤ 5 minutes away” were really 5–15 minutes away, so neighbors migrate outward into honest buckets. Second, coverage fills in: genuine medium-range pairs that were missing before now appear. The net effect is fewer phantom close-by drivers and more genuine medium-range options.
The refresh also visibly reshaped the size of the dataset, region by region.
With the change validated, the work now is to roll the refreshed dataset out to the remaining regions, settle each into a new equilibrium, and automate the refresh on a six-month cycle so the dataset never drifts a half-decade out of date again.
Where we’re going next: from static to time-aware ETAs
There’s one assumption baked into everything above that is obviously wrong if you’ve ever driven in a city: that there’s a single travel time between two neighborhoods. In reality, the trip that takes 8 minutes at 3 a.m. can take 25 at evening rush. The current version still produces one ETA file per region, a single all-hours average. Our next step makes the dataset time-aware. Pricing’s Graph already reasons in terms of nine time categories based on the hour of the week (morning commute, weekday day, evening commute, weekend daytime, and so on). The next version generates a separate Geohash ETA file per time category, nine files per region, so that each one reflects the traffic dynamics of its slice of the week. The historical-ETA computation is bucketed by time category, and where we fall back to simulation, the sampled requests are drawn from within the same time category as the estimate they’re filling in.
A few deliberate design choices make this practical:
- Nine files, not one nine-times-bigger file. Splitting by time category means a consumer only loads the bucket it currently needs at inference time, instead of paying to read the whole week’s data on every model run. Both Pricing and the real-time supply team preferred this, since each can simply key its file path on region plus current time bucket.
- A shared library for the mapping. Converting a real-world moment (and its timezone) into the right time category is the kind of logic you only want to write once, so it lives in a shared marketplace library that any consumer can call. It handles the UTC-to-local conversion and daylight-saving edge cases so individual teams don’t have to.
- Latency stays flat. The trade-off is memory, roughly a couple of extra gigabytes to hold a bucket, but with simple prefetching (warm the next time category’s file before the clock rolls into it), read latency stays about the same as the single-file world.
Early validation of the time-aware files is encouraging, and it points at the longer-term direction for this dataset. We’re also exploring more advanced ways to compute the estimates themselves, for example, weighted ETAs, where more recent observations carry more weight than older ones, so the dataset reflects how a city is moving now rather than averaging flatly over a year of history. The arc is consistent: from a single frozen snapshot, to a periodically refreshed and geographically honest map, to one that moves with the time of day, and, eventually, toward more real-time and adaptive ETAs.
Conclusion
Neighborhood Reachability Signals are a small piece of infrastructure with an outsized blast radius. Letting them drift to a 2019 snapshot had a real cost in mispriced markets, missed matches, and even maps that drew demand over open water. The redesign fixed the foundations: drivable geohashes from the source of truth, far better coverage, and new fields that let consumers shape the data themselves instead of maintaining their own patches. Just as important, the dataset now refreshes on a real cadence instead of by one-off fixes. Making the files time-aware is the natural next step, letting travel time vary with the day, the way it always has in the real world.
Acknowledgements
We would like to thank all our existing and past real-time and forecasting team members (Brian, Jim, Jeff, Josh, Hongru, Casey) and also our partner teams like Pricing (Xiangnan, Simon) and Driver Earnings for their valuable feedback
Want to build systems that balance a real-time marketplace at scale? Join us at Lyft.
Refreshing the Travel-Time Map Behind Lyft’s Marketplace: Rebuilding Neighborhood Reachability… was originally published in Lyft Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.
Source: eng.lyft.com
