Venkatesh♡
All blogs
2026-10-1413 min read

How Uber and Rapido Find Nearby Drivers in Real Time

How ride apps find nearby drivers in milliseconds: Geohash narrows the search, Redis stores live locations and Haversine filters by radius. Worked example included.

What you'll use

GeohashRedisHaversineNode.jsngeohashGEOSEARCH
01

The problem: finding nearby drivers

Imagine you book a ride in Hyderabad. There are thousands of drivers online across the city. How does the backend find drivers near you within seconds, without checking every driver's location one by one?

Every driver continuously shares their GPS coordinates:

  • Latitude
  • Longitude

If the backend calculated the distance between the passenger and every online driver for every ride request, the computation would become expensive as the number of drivers grows. We need a way to narrow down the search before calculating distances.

That's where Geohash helps. In this article you'll see how Geohash, Redis and the Haversine formula work together.

02

How does Geohash work?

Geohash divides the Earth's surface into rectangular grid cells and represents each cell using a string of characters. For example, our passenger's pickup location is:

Pickup
17.40044, 78.56073
Pickup Geohash
tepfgx
Deep diveCheck how these coordinates convert into the Geohash tepfgx, bit by bit

Suppose four drivers have the following locations:

DriverLatitude, LongitudeGeohash
Driver A17.41232, 78.54825tepg53
Driver B17.40694, 78.59361tepgh2
Driver C17.39912, 78.60995tepfuz
Driver D17.42066, 78.52963tepg4g

Each driver belongs to a Geohash cell based on their coordinates. Instead of searching across the entire city, the backend can search the pickup cell and the relevant neighbouring cells. This significantly reduces the number of driver locations that need to be considered.

03

Where does Redis come in?

A real-time driver-tracking system needs fast access to frequently changing GPS locations. Redis can store each driver's latest location and associated Geohash. Whenever a driver sends a new GPS update, the backend updates their location in Redis.

When a passenger requests a ride, the backend:

  1. Reads the pickup coordinates.
  2. Identifies the pickup Geohash cell.
  3. Searches the cells that cover the requested search radius.
  4. Retrieves candidate drivers from those cells.
  5. Calculates the actual distance to each candidate.
  6. Filters out drivers outside the requested radius.
IMPORTANTRedis is the fast storage layer in this example. The system also needs a suitable spatial or Geohash-based index to retrieve nearby candidates efficiently.

Redis ships with exactly that kind of index: the GEO commands. GEOADD stores each driver in a sorted set, using a Geohash-derived score, and GEOSEARCH (Redis 6.2+) returns the members inside a radius. Note that Redis takes longitude first.

redis-cli
# longitude first, then latitude
GEOADD drivers:online 78.54825 17.41232 driver:A
GEOADD drivers:online 78.59361 17.40694 driver:B
GEOADD drivers:online 78.60995 17.39912 driver:C
GEOADD drivers:online 78.52963 17.42066 driver:D

# drivers within 2 km of the pickup, nearest first
GEOSEARCH drivers:online FROMLONLAT 78.56073 17.40044 BYRADIUS 2 km ASC WITHDIST

# 1) "driver:A"
# 2) "1.8707"

Redis does the radius filtering for you here. The rest of this article shows what that filtering actually means.

04

Why do we need neighbouring Geohash cells?

Our pickup Geohash is tepfgx, but Driver A's Geohash is tepg53. They are different strings, yet Driver A is close to the pickup location.

This is an important property of Geohash: two geographically close points can belong to different cells, especially when they lie on opposite sides of a cell boundary. Searching only the exact pickup cell can therefore miss nearby drivers. The backend must search the relevant neighbouring cells as well.

Also, sharing a Geohash prefix does not guarantee that two locations are close. A prefix can help narrow a search, but it cannot replace a proper radius search. Compare Driver A and Driver C against the pickup tepfgx:

DriverGeohashShared prefixActual distance
Driver Atepg53tep (3 characters)1.87 km
Driver Ctepfuztepf (4 characters)5.22 km

tepf identifies a four-character grid cell. Both the pickup location and Driver C fall inside it. tepg is a different four-character cell, and Driver A falls in that one. Think of Geohash cells as squares on a map: two points on opposite sides of a boundary can be very close even though their Geohashes differ, while two points inside the same larger square can be several kilometres apart.

TIPChoose the precision to match your radius. A 6-character cell here is only about 1.17 km wide and 0.61 km tall, so the 8 neighbours of tepfgx do not include Driver A's cell tepg53, even though Driver A is 1.87 km away. With 5 characters (cells of about 4.7 × 4.9 km), the pickup cell tepfg and its 8 neighbours (tepfd, tepfe, tepff, tepfs, tepfu, tepg4, tepg5, tepgh) cover all four drivers, and the distance filter then keeps only Driver A.

So the correct approach in your application is:

  1. Encode the user's coordinates with ngeohash.
  2. Use Geohash prefixes and neighbouring cells to narrow the search.
  3. Calculate the actual distance to the candidate locations.
  4. Sort by distance and return the nearest one.

Don't sort Geohash strings alphabetically, and don't pick the one with the longest matching prefix. Geohash organises and filters locations. The actual distance calculation decides which one is nearest.

05

How do we calculate the actual distance?

Finding candidate drivers is only the first step. Suppose the passenger wants a driver within a 2 km radius. Let's calculate the distance to Driver C.

Pickup coordinates
17.40044, 78.56073
Driver C coordinates
17.39912, 78.60995

The coordinate differences are approximately:

  • Latitude: 0.00132° ≈ 0.147 km
  • Longitude: 0.04922° ≈ 5.22 km east-west at this latitude

The backend uses a geographical distance formula, such as the Haversine formula, to calculate the straight-line distance between the two coordinates. The calculated distance is approximately 5.22 km.

Deep diveSee the full Haversine calculation for Driver C, step by step

The requested radius is 2 km, so Driver C is excluded. Now consider Driver A. The calculated distance between the pickup location and Driver A is approximately 1.87 km. Since 1.87 km is less than 2 km, Driver A passes the distance filter.

NOTEHaversine gives the straight-line (great-circle) distance, not the road distance. It is a fast way to filter candidates. Road distance and ETA come from a routing service afterwards.
06

What happens to the drivers?

Here is the result after calculating the distances using the coordinates above, with a 2 km radius:

DriverGeohashApprox. distanceResult
Atepg531.87 kmKeep
Btepgh23.56 kmExclude
Ctepfuz5.22 kmExclude
Dtepg4g3.99 kmExclude

The system keeps Driver A as a candidate because the driver is within the requested 2 km radius. The other drivers are excluded because they are farther away.

In a real implementation, drivers whose cells cannot intersect the search area can be eliminated before their exact distances are calculated.

07

The complete system design flow

  1. Driver GPS location

  2. Convert coordinates to Geohash

  3. Update the latest location in Redis

  4. Passenger requests a ride

  5. Search the pickup cell and relevant neighbouring cells

  6. Retrieve candidate drivers

  7. Calculate the actual distance using Haversine

  8. Apply the requested radius filter

  9. Rank eligible drivers and match a driver

Key takeaway

Each component has a different responsibility:

ComponentResponsibility
GeohashNarrows the geographical search area.
RedisProvides fast access to frequently updated driver locations.
Haversine formulaCalculates the approximate straight-line distance between two GPS coordinates.
Radius filterRemoves candidates outside the requested distance.
Don't calculate the distance to every driver first. Narrow down the candidates geographically, then calculate their actual distances.

Together, these techniques make an efficient nearby-driver discovery system, and a useful building block for ride-hailing platforms, delivery applications and other real-time location-based systems.

In short

  1. 1

    Drivers send GPS coordinates

    Latitude + longitude

  2. 2

    Convert to Geohash and update Redis

    Store each driver's latest location

  3. 3

    Passenger requests a ride

    Pickup coordinates → tepfgx

  4. 4

    Search the relevant Geohash cells

    Retrieve nearby driver candidates

  5. 5

    Calculate distance and filter

    Keep suitable drivers within the radius

  6. 6

    Match an available driver

    Send the ride request

08

Haversine calculation, step by step

Here is the complete Haversine calculation for Driver C, using the exact pickup and driver coordinates.

1. Given coordinates

Pickup latitude φ₁
17.40044
Pickup longitude λ₁
78.56073
Driver C latitude φ₂
17.39912
Driver C longitude λ₂
78.60995
Earth's mean radius R
6371 km

2. Calculate the coordinate differences

formula
Δφ = φ₂ − φ₁ = 17.39912 − 17.40044 = −0.00132°
Δλ = λ₂ − λ₁ = 78.60995 − 78.56073 = 0.04922°

3. Convert degrees into radians

The Haversine formula needs radians, not degrees. Radians = degrees × π / 180.

formula
Δφ = −0.00132 × π/180 ≈ −0.0000230383
Δλ =  0.04922 × π/180 ≈  0.00085905
φ₁ = 17.40044 × π/180 ≈  0.303695
φ₂ = 17.39912 × π/180 ≈  0.303672

4. Apply the Haversine formula

formula
a = sin²(Δφ / 2) + cos(φ₁) · cos(φ₂) · sin²(Δλ / 2)

Calculate each part.

formula
Part A (latitude component)
  Δφ / 2 = −0.0000115192
  sin²(−0.0000115192) ≈ 1.3269 × 10⁻¹⁰

Part B (longitude component)
  Δλ / 2 = 0.00042953
  sin²(0.00042953) ≈ 1.8449 × 10⁻⁷

Part C (cosines)
  cos(0.303695) ≈ 0.954238
  cos(0.303672) ≈ 0.954245
  cos(φ₁) · cos(φ₂) ≈ 0.910577
  0.910577 × 1.8449 × 10⁻⁷ ≈ 1.6799 × 10⁻⁷

Part D (add both components)
  a ≈ 1.3269 × 10⁻¹⁰ + 1.6799 × 10⁻⁷
  a ≈ 1.6813 × 10⁻⁷

5. Calculate the distance

formula
d = 2R · arcsin(√a)

√a = √(1.6813 × 10⁻⁷) ≈ 0.00041003
arcsin(0.00041003) ≈ 0.00041003   (for tiny angles, arcsin(x) ≈ x)

d = 2 × 6371 × 0.00041003
d ≈ 5.22 km

6. Apply the 2 km radius filter

The actual distance is approximately 5.22 km and the requested radius is 2 km. Since 5.22 > 2, Driver C is excluded.

NOTEIntermediate values above are rounded for readability. With full-precision values the result is 5.2246 km.

The same calculation in JavaScript

javascript
function haversineKm(lat1, lon1, lat2, lon2) {
  const R = 6371; // Earth's mean radius in km
  const toRad = (deg) => (deg * Math.PI) / 180;

  const dLat = toRad(lat2 - lat1);
  const dLon = toRad(lon2 - lon1);

  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2;

  return 2 * R * Math.asin(Math.sqrt(a));
}

// pickup -> Driver C
haversineKm(17.40044, 78.56073, 17.39912, 78.60995); // 5.2246 km  (excluded)
// pickup -> Driver A
haversineKm(17.40044, 78.56073, 17.41232, 78.54825); // 1.8704 km  (kept)
Back to the articleReturn to “How do we calculate the actual distance?”
09

Geohash encoding, bit by bit

Let's convert the pickup location into the Geohash tepfgx step by step, showing every range division, every binary bit and every character.

How Geohash turns coordinates into characters

Geohash uses a special Base32 alphabet. There are 32 characters, and the letters a, i, l and o are excluded. The characters used by our pickup Geohash are highlighted.

00
11
22
33
44
55
66
77
88
99
10b
11c
12d
13e
14f
15g
16h
17j
18k
19m
20n
21p
22q
23r
24s
25t
26u
27v
28w
29x
30y
31z
  • Each character represents 5 binary bits.
  • The algorithm generates bits by dividing geographic ranges in half, alternating between longitude and latitude.
  • Longitude starts in the range [−180, 180]. Latitude starts in the range [−90, 90].
  • If the coordinate is in the upper half, write 1. Otherwise write 0. Then repeat with the smaller range.
  • Group every 5 bits and convert each group to one Base32 character.

A six-character Geohash therefore represents 30 bits: 15 for longitude and 15 for latitude.

The input

Latitude
17.400441666657823
Longitude
78.56072868878287

These are the full-precision coordinates. The rounded pickup 17.40044, 78.56073 used earlier produces the same Geohash.

Step A: divide the ranges

Initially, longitude runs from −180 to +180 and latitude from −90 to +90. The first longitude midpoint is (−180 + 180) / 2 = 0. The longitude 78.5607 is greater than 0, so the first bit is 1 and the algorithm keeps the upper half, 0 to 180.

Next it divides latitude at 0. The latitude 17.4004 is greater than 0, so the second bit is also 1. The algorithm keeps alternating longitude and latitude, halving the selected range each time.

Every one of the 30 divisions

  1. Calculate the midpoint: (minimum + maximum) / 2.
  2. If the coordinate is greater than or equal to the midpoint, output 1 and keep the upper half. Otherwise output 0 and keep the lower half.
  3. Alternate longitude, latitude, longitude, latitude until 30 bits are produced.
StepAxisRange beforeMidpointComparisonBit
1Longitude-180 to 180078.5607 ≥ 01
2Latitude-90 to 90017.4004 ≥ 01
3Longitude0 to 1809078.5607 < 900
4Latitude0 to 904517.4004 < 450
5Longitude0 to 904578.5607 ≥ 451
6Latitude0 to 4522.517.4004 < 22.50
7Longitude45 to 9067.578.5607 ≥ 67.51
8Latitude0 to 22.511.2517.4004 ≥ 11.251
9Longitude67.5 to 9078.7578.5607 < 78.750
10Latitude11.25 to 22.516.87517.4004 ≥ 16.8751
11Longitude67.5 to 78.7573.12578.5607 ≥ 73.1251
12Latitude16.875 to 22.519.687517.4004 < 19.68750
13Longitude73.125 to 78.7575.937578.5607 ≥ 75.93751
14Latitude16.875 to 19.687518.2812517.4004 < 18.281250
15Longitude75.9375 to 78.7577.3437578.5607 ≥ 77.343751
16Latitude16.875 to 18.2812517.57812517.4004 < 17.5781250
17Longitude77.34375 to 78.7578.04687578.5607 ≥ 78.0468751
18Latitude16.875 to 17.57812517.226562517.4004 ≥ 17.22656251
19Longitude78.046875 to 78.7578.398437578.5607 ≥ 78.39843751
20Latitude17.2265625 to 17.57812517.4023437517.4004 < 17.402343750
21Longitude78.3984375 to 78.7578.5742187578.5607 < 78.574218750
22Latitude17.2265625 to 17.4023437517.31445312517.4004 ≥ 17.3144531251
23Longitude78.3984375 to 78.5742187578.48632812578.5607 ≥ 78.4863281251
24Latitude17.314453125 to 17.4023437517.358398437517.4004 ≥ 17.35839843751
25Longitude78.486328125 to 78.5742187578.530273437578.5607 ≥ 78.53027343751
26Latitude17.3583984375 to 17.4023437517.3803710937517.4004 ≥ 17.380371093751
27Longitude78.5302734375 to 78.5742187578.5522460937578.5607 ≥ 78.552246093751
28Latitude17.38037109375 to 17.4023437517.39135742187517.4004 ≥ 17.3913574218751
29Longitude78.55224609375 to 78.5742187578.56323242187578.5607 < 78.5632324218750
30Latitude17.391357421875 to 17.4023437517.396850585937517.4004 ≥ 17.39685058593751

Step B: combine all 30 bits

Take the bit from every row, in order, without changing anything:

bits
110010110110101011100111111101

Split it into groups of five bits. Why five? Because 2⁵ = 32, so five bits can represent exactly 32 different values, from 0 to 31.

groups
11001  01101  10101  01110  01111  11101

Step C: convert each group to a character

In a five-bit group the place values are 16, 8, 4, 2 and 1. For example, 11001 = 1×16 + 1×8 + 0×4 + 0×2 + 1×1 = 25. In the Base32 alphabet above, index 25 is t. Do the same for all six groups:

GroupBitsCalculationDecimalCharacter
11100116 + 8 + 125t
2011018 + 4 + 113e
31010116 + 4 + 121p
4011108 + 4 + 214f
5011118 + 4 + 2 + 115g
61110116 + 8 + 4 + 129x

Join the characters in the same order and the final Geohash is tepfgx: 30 bits, 6 groups, 6 characters.

Verify it with ngeohash

javascript
// npm i ngeohash
const ngeohash = require("ngeohash");

const latitude = 17.400441666657823;
const longitude = 78.56072868878287;

const geohash = ngeohash.encode(latitude, longitude, 6);
console.log(geohash); // tepfgx

console.log(ngeohash.decode(geohash));
// { latitude: 17.39959..., longitude: 78.55773..., error: {...} }
// the centre of the Geohash cell, not your exact point

console.log(ngeohash.neighbors("tepfg"));
// the 8 cells around a 5-character cell:
// [ 'tepg5', 'tepgh', 'tepfu', 'tepfs', 'tepfe', 'tepfd', 'tepff', 'tepg4' ]

The package performs the same repeated range divisions and Base32 conversion internally.

TIPThe letters are labels assigned to five-bit binary values. They are not chosen by the spelling of a place or by distance. Geohash encodes a position inside a grid, and the actual distance must be calculated separately.
Back to the articleReturn to “How does Geohash work?”

Related blog

BookMyShow: HLD Breakdown

Read next

Scrolled Too Far

Have an Idea? Let’s Turn It Into Something Real.