Skip to content

Query Features

How to filter, paginate, and sort features from any ADAM collection. This guide is a series of focused recipes — pick the one that matches your need.

The endpoint everything here builds on:

GET /collections/{collectionId}/items

Response is always a GeoJSON FeatureCollection with numberMatched, numberReturned and pagination links.

Discover what you can filter on

Before you filter, ask the collection what is filterable:

GET /collections/{collectionId}/queryables

The response is a JSON Schema. Every property that appears at properties.{name} is a valid operand for the filter= query parameter.

import httpx

BASE = "https://api.adam.geospatial.wfp.org/api"
qs = httpx.get(f"{BASE}/collections/adam.adam_eq_events/queryables").json()
print(list(qs["properties"].keys()))

Recipe — bounding box

Spatial filter, minLon,minLat,maxLon,maxLat, WGS84:

GET /collections/adam.adam_eq_events/items?bbox=26,36,45,42

For extents that cross the antimeridian (180°), pass a single bbox where minLon > maxLon:

?bbox=170,-20,-170,20

Recipe — temporal window

Single instant:

?datetime=2026-04-15T12:00:00Z

Closed interval:

?datetime=2026-04-01T00:00:00Z/2026-04-30T23:59:59Z

Open-ended (useful for "everything since ..."):

?datetime=2026-04-01T00:00:00Z/..

Recipe — attribute filter (CQL2)

Use the filter= parameter with OGC CQL2 syntax. Operands must appear in /queryables.

?filter=mag >= 6.0

Combine conditions with AND / OR:

?filter=mag >= 6.0 AND depth < 30

Remember to URL-encode the filter value when constructing URLs by hand — httpx does it for you.

Recipe — combine spatial, temporal and attribute filters

All three parameters stack, intersected:

resp = httpx.get(
    f"{BASE}/collections/adam.adam_eq_events/items",
    params={
        "bbox": "26,36,45,42",
        "datetime": "2026-01-01T00:00:00Z/..",
        "filter": "mag >= 6.0",
        "sortby": "-datetime",
        "limit": 50,
    },
)
fc = resp.json()

Recipe — paginate through all results

items returns at most limit features. For anything unbounded, paginate with offset and the numberMatched the response returns:

def iter_all(url, params, page_size=500):
    offset = 0
    while True:
        page = httpx.get(url, params={**params, "limit": page_size, "offset": offset}).json()
        for f in page["features"]:
            yield f
        offset += page["numberReturned"]
        if offset >= page["numberMatched"] or page["numberReturned"] == 0:
            return

for feature in iter_all(
    f"{BASE}/collections/adam.adam_eq_events/items",
    {"datetime": "2026-01-01T00:00:00Z/.."},
):
    ...

Recipe — sort

Prefix a queryable name with - for descending order:

?sortby=-datetime       # most recent first
?sortby=mag             # ascending

Recipe — select output format

Most endpoints do content negotiation via the f parameter:

?f=json        # machine-readable JSON (often the default)
?f=geojson     # explicit GeoJSON
?f=html        # the live HTML browser (see the Quickstart)

When things go wrong

  • 400 Bad Request — usually an invalid filter expression or a malformed bbox/datetime. Check names against /queryables.
  • Empty results — the filters intersect to nothing. Open the collection's HTML browser to confirm there is data for your window.
  • Slow paginated runs — reduce limit or add tighter bbox/datetime. Avoid full-catalogue scans during peak hours.

Next steps

Live references