Skip to content

Tropical storm hazard icon Tropical Storms example

Business question

Which tropical storms of Red alert level have we been tracking in the last 30 days, and how does peak wind speed compare across them?

We use adam.adam_ts_events for the events themselves and adam.adam_ts_tracks for the forecast tracks — both linked by the same event identifier.

Step-by-step from a terminal

1. List recent Red-alert storms

curl -sS -H "Accept: application/geo+json" \ "https://api.adam.geospatial.wfp.org/api/collections/adam.adam_ts_events/items?limit=5&sortby=-wind_speed&filter=alert_level%20%3D%20'Red'%20AND%20published_at%20%3E%20TIMESTAMP('2026-03-21T00:00:00')" \ | jq '{numberMatched, sample: (.features[0].properties | {name, published_at, current_storm_status, alert_level, wind_speed, iso3, cleared})}'
{
"numberMatched": 20,
"sample": {
"name": "SINLAKU-26",
"published_at": "2026-04-15T02:21:45",
"current_storm_status": "Category 3",
"alert_level": "Red",
"wind_speed": 203.7024,
"iso3": "MNP",
"cleared": false
}
}

2. Follow a named storm across collections

Once you have a storm name, you can pull its track geometries from the sibling tracks collection:

curl -sS -H "Accept: application/geo+json" \ "https://api.adam.geospatial.wfp.org/api/collections/adam.adam_ts_tracks/items?limit=100&filter=name%20%3D%20'SINLAKU-26'" \ | jq '{numberMatched, fields: (.features[0].properties | keys)}'

And the forecast nodes (with the node_time <> 'previous position' trick to skip the history):

curl -sS -H "Accept: application/geo+json" \ "https://api.adam.geospatial.wfp.org/api/collections/adam.adam_ts_nodes/items?limit=100&filter=name%20%3D%20'SINLAKU-26'%20AND%20node_time%20%3C%3E%20'previous%20position'" \ | jq '.features[].properties | {name, node_time, wind_speed, storm_status}' \ | head -40

Python recap

import json
import urllib.parse
import urllib.request
from datetime import datetime, timedelta, timezone

BASE = "https://api.adam.geospatial.wfp.org/api"
since = (datetime.now(timezone.utc) - timedelta(days=30)).strftime("%Y-%m-%dT%H:%M:%S")

params = urllib.parse.urlencode({
    "filter": f"alert_level = 'Red' AND published_at > TIMESTAMP('{since}')",
    "sortby": "-wind_speed",
    "limit": 50,
})
url = f"{BASE}/collections/adam.adam_ts_events/items?{params}"
req = urllib.request.Request(url, headers={"Accept": "application/geo+json"})

with urllib.request.urlopen(req) as r:
    fc = json.loads(r.read())

for f in fc["features"][:10]:
    p = f["properties"]
    print(f"  {p.get('name', ''):<15} {p.get('current_storm_status', ''):<15} {p.get('wind_speed', 0):>6.1f} km/h")

Interactive — marimo playground

Change the alert level dropdown or slide the date window; the query and chart update reactively.

Download: tropical-storms.py

Try it yourself

Live references

Next steps