Redis

Finding Nearby Points with Redis Geospatial Index

June 9, 2026
6 min read

"Find restaurants near me." This simple request hides complex math: converting coordinates to distances, filtering by radius, sorting by proximity. Redis Geospatial commands handle all of this natively, with results in milliseconds even across millions of locations.

How Redis Stores Geo Data

Redis doesn't use a spatial index or B-tree. Instead, it encodes latitude/longitude pairs as geohashes — a compact integer representation of a geographic cell — and stores them in a Sorted Set using the geohash as the score. This clever encoding allows efficient radius searches using sorted set range queries.

Core Geospatial Commands

# GEOADD: add locations (longitude, latitude, name)
GEOADD restaurants -73.9857 40.7484 "Times Square Diner"
GEOADD restaurants -73.9712 40.7614 "Central Park Cafe"
GEOADD restaurants -74.0059 40.7128 "Downtown Grill"
GEOADD restaurants -73.9442 40.6782 "Brooklyn Bistro"

# GEOPOS: retrieve stored coordinates
GEOPOS restaurants "Times Square Diner"
# 1) 1) "-73.98569941520690918"
#    2) "40.74839875265699012"

# GEODIST: distance between two points
GEODIST restaurants "Times Square Diner" "Central Park Cafe" km
# "1.6821"

# GEOSEARCH: find locations within a radius (Redis 6.2+)
GEOSEARCH restaurants FROMMEMBER "Times Square Diner" BYRADIUS 2 km ASC COUNT 5
# 1) "Times Square Diner"
# 2) "Central Park Cafe"

GEOSEARCH with Distance

# Search from coordinates (user's location) with distances
GEOSEARCH restaurants
  FROMLONLAT -73.9857 40.7484
  BYRADIUS 3 km
  ASC
  COUNT 10
  WITHCOORD
  WITHDIST

# Returns: name, distance, coordinates for each result

Python Implementation: Nearby Restaurant Finder

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def add_restaurant(name, lat, lon, metadata=None):
    r.geoadd("restaurants", [lon, lat, name])
    if metadata:
        r.hset(f"restaurant:{name}", mapping=metadata)

def find_nearby(user_lat, user_lon, radius_km=5, limit=10):
    results = r.geosearch(
        "restaurants",
        longitude=user_lon,
        latitude=user_lat,
        radius=radius_km,
        unit="km",
        sort="ASC",
        count=limit,
        withcoord=True,
        withdist=True
    )

    nearby = []
    for name, dist, (lon, lat) in results:
        meta = r.hgetall(f"restaurant:{name}")
        nearby.append({
            "name": name,
            "distance_km": round(float(dist), 2),
            "lat": lat,
            "lon": lon,
            **meta
        })
    return nearby

# Seed data
add_restaurant("Times Square Diner", 40.7484, -73.9857,
               {"cuisine": "American", "rating": "4.2"})
add_restaurant("Central Park Cafe", 40.7614, -73.9712,
               {"cuisine": "Italian", "rating": "4.7"})

# Find restaurants within 3km of user
user_location = (40.7500, -73.9800)
results = find_nearby(*user_location, radius_km=3)
for r in results:
    print(f"{r['name']} — {r['distance_km']}km ({r['cuisine']})")

Real-World Use Cases

  • Ride-sharing — find available drivers within 2km of a passenger; update driver positions on every GPS ping with GEOADD
  • Delivery tracking — show which delivery zones a user falls within; estimate delivery time based on courier proximity
  • Store locator — "find nearest 5 stores" on every e-commerce site
  • Social features — "people near you" or location-tagged content within a radius
  • Real-time asset tracking — warehouse equipment, fleet vehicles, IoT devices

Key Takeaways

  • GEOADD stores locations as geohash-encoded sorted set members
  • GEOSEARCH (Redis 6.2+) replaces GEORADIUS — supports radius and bounding box queries from coordinates or an existing member
  • GEODIST calculates the distance between any two stored points in m, km, mi, or ft
  • Store additional metadata (name, rating, hours) in a separate Hash keyed by location name
  • For moving targets (drivers, couriers), GEOADD on each update simply overwrites the old position