For AI agents: this page is also available as Markdown at /docs/geo-search.md. Every docs page is: add .md to its URL. llms.txt lists them all.

Geo search

Geo search finds documents by where they are. A location is a geo_point field and an area or route is a geo_shape field. A geo condition is a query like any other: a filter beside a text query ("coffee within 5 km"), the whole query ("everything in this polygon"), a sort key ("nearest first"), a scoring signal ("prefer closer results") or an aggregation ("matches per map cell"). It runs in the same request, over the same consistent view of the collection, as your text queries, filters and facets.

What you can do

Feature What it does
Distance queries Match points within a radius of a center.
Bounding boxes Match points inside a latitude and longitude box, including boxes that cross the date line.
Point formats Send points as [lon, lat], objects, GeoJSON, WKT, "lat,lon" or geohashes, and type fields by the _p and _ps suffixes.
Sort by distance Order hits nearest (or farthest) first.
Distance decay scoring Blend text relevance with closeness: full score near a point, fading with distance.
Query buckets by distance or area Count matches within each of several radii or boxes.
Polygons and shapes Match points inside, or outside, a polygon, multipolygon, circle or envelope, or along a line.
Grid cells Match the points in one geohash, map tile or H3 cell.
Distances in hits Each hit of a distance sort carries its distance; /v1 gets a geo_distance sort object.
Nearest k The k documents nearest a point, with filters applied first.
distance_feature A scoring clause that rewards closeness without narrowing the matches.
Grid aggregations Geohash, map tile and H3 heat-map buckets, with metrics and facets per cell.
Distance rings Counts per distance band from a point.
Bounds and centroid The box around the matches and their average location.
Shape fields Index areas and lines, such as delivery zones, and query them by intersects, disjoint, within or contains.

Pages in this guide

Which API to use

Geo queries have no form in Lucene syntax or SQL. Write them as Elasticsearch Query DSL, or use Solr's spatial parameters:

Where How
WarpSearch API (/v1/collections/{c}/search) Set "language": "es" and put geo queries in query, filters or post_filters. The geo facet types, geo metrics and the geo_distance sort object work with any language.
Elasticsearch and OpenSearch endpoints Geo queries, the _geo_distance sort and geo aggregations, answered in Elasticsearch's shapes.
Solr endpoint, or "language": "solr" {!geofilt}, {!bbox} and sorting by geodist().

The MCP search tool takes the same geo queries, facets, metrics and sort; see Guide for AI agents. Searches with geo queries have no native replay form; see Native queries and replays.

Choosing a feature

Quickstart

The examples in this guide use one collection, places: cafes, shops and bakeries around New York, each with a location. Geo shapes adds a delivery area to some of them.

Create the collection with a geo_point field. index: "range" builds a points index, which makes distance and box queries fast on large collections:

curl -X PUT https://api.warpsearch.io/v1/collections/places \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "schema": {
      "fields": {
        "name":        "text",
        "description": "text",
        "category":    "string",
        "city":        "string",
        "rating":      "double",
        "location":    {"type": "geo_point", "index": "range"}
      }
    }
  }'

Load a few places. A point is [longitude, latitude], longitude first:

curl -X POST "https://api.warpsearch.io/v1/collections/places/documents?commit=true" \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {"id": "p1", "name": "Harbor Coffee", "description": "Espresso, pour-over and pastries",
     "category": "cafe", "city": "Manhattan", "rating": 4.6, "location": [-73.9851, 40.7589]},
    {"id": "p2", "name": "Bridge Roasters", "description": "Coffee roastery with a cafe under the bridge",
     "category": "cafe", "city": "Brooklyn", "rating": 4.4, "location": [-73.9897, 40.7033]},
    {"id": "p3", "name": "Midtown Market", "description": "Groceries, deli and a coffee bar",
     "category": "grocery", "city": "Manhattan", "rating": 4.1, "location": [-73.9772, 40.7527]},
    {"id": "p4", "name": "Astoria Bakery", "description": "Bread, pastries and coffee",
     "category": "bakery", "city": "Queens", "rating": 4.7, "location": [-73.9235, 40.7644]},
    {"id": "p5", "name": "Hudson Espresso", "description": "Espresso bar by the ferry",
     "category": "cafe", "city": "Hoboken", "rating": 4.5, "location": [-74.0324, 40.7440]}
  ]'

Find the cafes within 5 km of Times Square (40.7580, -73.9855), nearest first:

curl -X POST https://api.warpsearch.io/v1/collections/places/search \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "language": "es",
    "query": {
      "query": {"bool": {"filter": [
        {"term": {"category": "cafe"}},
        {"geo_distance": {"distance": "5km", "location": "40.7580,-73.9855"}}
      ]}},
      "sort": [{"_geo_distance": {"location": "40.7580,-73.9855", "order": "asc", "unit": "km"}}]
    },
    "fields": ["name", "city"]
  }'
curl -X POST https://api.warpsearch.io/compat/es/places/_search \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {"bool": {"filter": [
      {"term": {"category": "cafe"}},
      {"geo_distance": {"distance": "5km", "location": "40.7580,-73.9855"}}
    ]}},
    "sort": [{"_geo_distance": {"location": "40.7580,-73.9855", "order": "asc", "unit": "km"}}],
    "_source": ["name", "city"]
  }'

The same body works on /compat/opensearch/places/_search.

d is in kilometers and pt is lat,lon:

curl "https://api.warpsearch.io/compat/solr/places/select" \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  --data-urlencode "q=*:*" \
  --data-urlencode "fq=category:cafe" \
  --data-urlencode "fq={!geofilt sfield=location pt=40.7580,-73.9855 d=5}" \
  --data-urlencode "sort=geodist(location,40.7580,-73.9855) asc" \
  --data-urlencode "fl=id,name,city"

The hits are p1 (Harbor Coffee, about 0.1 km away), then p5 (Hudson Espresso, about 4.2 km). Bridge Roasters, 6.1 km away, is outside the radius.

Limits

These apply to geo queries and aggregations:

Limit Value
Vertices in a query shape or polygon 10000
k of geo_nearest Your plan's result window
Cells a grid aggregation returns Your plan's maximum facet size. The default size is 10000, or that maximum if it is lower
Distinct cells a grid aggregation counts 65536
Ranges of a distance-ring aggregation Your plan's maximum facet size

Going past a limit answers 422 limit_exceeded, except the distinct-cell count, which fails the search with an error. See Limits and errors.

Distances are great-circle (haversine) distances. Elasticsearch's distance_type: plane is accepted with a warning. Query centers can be "lat,lon", [lon, lat], {"lat": ..., "lon": ...} or "POINT (lon lat)"; geohash centers are rejected.

View this page as Markdown

Cookie settings

Optional tracking stays off until you choose it. Your choice applies to this browser for 180 days. Read the details.

Essential storage keeps you signed in, protects forms, and remembers this choice. It is always available.