Tropical cyclone tracking and intensity data. Spire aggregates data from the NHC (NOAA/NWS), CPHC, and JTWC to provide global coverage of tropical cyclone activity, including historical tracks dating back to 1990.
Endpoints¶
| Method | Endpoint | Description |
|---|---|---|
| GET | /storm/json | Get storm data as JSON |
| GET | /storm/kmz | Get storm data as KMZ |
Storm JSON¶
GET /storm/jsonRetrieve tropical cyclone track and intensity information as JSON. Storms are identified by their ATCF ID.
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | active (default), inactive, or all |
id | string | No | ATCF ID (e.g., AL022024) |
name | string | No | Storm name (case insensitive, does not include category) |
category | string | No | Minimum category reached (returns storms at this level or higher) |
intensity | number | No | Minimum sustained wind speed (knots) reached at any point |
region | string | No | Basin or sub-basin code |
period | string | No | ISO 8601 time range (e.g., 2023-01-01T00:00:00/2023-07-15T00:00:00) |
data_filter | string | No | all (default), analysis, or forecast |
limit | integer | No | Max storms to return (1–100, default: 100) |
offset | integer | No | Number of storms to skip (for pagination) |
ATCF ID Format¶
Storm IDs follow the format BBXXYYYY:
BB— 2-character ATCF basin code (AL, EP, WP, CP, IO, SH)XX— 2-digit annual cyclone number (1–99, not reused until the following season)YYYY— 4-digit year the storm formed
Example: AL022024 = Atlantic basin, 2nd storm of 2024
Storm Categories¶
Categories are listed in order of increasing intensity. When filtering by category, all storms that reached that level or higher are returned.
| Code | Description |
|---|---|
XX | Unknown |
DS | Dissipating |
IN | Inland |
PT | Post-Tropical Cyclone |
LO | Low Pressure Center |
SS | Subtropical Storm |
TD | Tropical Depression |
TS | Tropical Storm |
TC1 | Category 1 Hurricane / Typhoon |
TC2 | Category 2 Hurricane / Typhoon |
TC3 | Category 3 Hurricane / Typhoon |
TC4 | Category 4 Hurricane / Typhoon |
TC5 | Category 5 Hurricane / Super Typhoon |
Basins¶
| Code | Name |
|---|---|
NA | North Atlantic |
SA | South Atlantic |
EP | Eastern North Pacific |
WP | Western North Pacific |
NI | North Indian |
SI | South Indian |
SP | Southern Pacific |
Sub-Basins¶
| Code | Name |
|---|---|
CS | Caribbean Sea |
GM | Gulf of Mexico |
MM | Missing (no sub-basin assigned) |
CP | Central Pacific |
AS | Arabian Sea |
BB | Bay of Bengal |
WA | Western Australia |
EA | Eastern Australia |
Example Requests¶
Get active storms:
curl -X GET \
'https://api.wx.spire.com/storm/json?status=active' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/storm/json",
params={"status": "active"},
headers={"spire-api-key": "YOUR_API_KEY"},
)
data = response.json()const response = await fetch(
"https://api.wx.spire.com/storm/json?status=active",
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();Get a specific storm by name:
curl -X GET \
'https://api.wx.spire.com/storm/json?name=Beryl&status=all' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/storm/json",
params={"name": "Beryl", "status": "all"},
headers={"spire-api-key": "YOUR_API_KEY"},
)
data = response.json()const response = await fetch(
"https://api.wx.spire.com/storm/json?name=Beryl&status=all",
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();Get storms in a time period:
curl -X GET \
'https://api.wx.spire.com/storm/json?period=2024-06-01T00:00:00/2024-11-30T00:00:00&status=inactive' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/storm/json",
params={
"period": "2024-06-01T00:00:00/2024-11-30T00:00:00",
"status": "inactive",
},
headers={"spire-api-key": "YOUR_API_KEY"},
)
data = response.json()const params = new URLSearchParams({
period: "2024-06-01T00:00:00/2024-11-30T00:00:00",
status: "inactive",
});
const response = await fetch(
`https://api.wx.spire.com/storm/json?${params}`,
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();Get major hurricanes (Category 3+):
curl -X GET \
'https://api.wx.spire.com/storm/json?category=TC3®ion=NA&status=all' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/storm/json",
params={"category": "TC3", "region": "NA", "status": "all"},
headers={"spire-api-key": "YOUR_API_KEY"},
)
data = response.json()const response = await fetch(
"https://api.wx.spire.com/storm/json?category=TC3®ion=NA&status=all",
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();Response¶
The response is an array of storm objects, each with separate observed_track and forecast_track arrays:
[
{
"atcf_id": "WP052023",
"storm_name": "Doksuri",
"basin": "WP",
"subbasin": "MM",
"season": 2023,
"is_active": true,
"advisory_number": 18,
"advisory_issuance_time": "2023-07-25T18:00:00+00:00",
"max_development_category": "TC4",
"max_sustained_wind": 128.0,
"min_mslp": 926.0,
"dates_active": {
"start": "2023-07-17",
"end": "ongoing"
},
"observed_track": [
{
"lat": 7.1,
"lon": 136.2,
"category_code": "XX",
"basin": "WP",
"subbasin": "MM",
"wind_gusts": null,
"max_sustained_wind": 20.0,
"min_mslp": 1004.0,
"movement_direction": null,
"movement_speed": null,
"wind_extent_radii": {},
"time": "2023-07-17T00:00:00+00:00"
}
],
"forecast_track": [
{
"lat": 19.6,
"lon": 120.6,
"category_code": "TC3",
"basin": "WP",
"subbasin": "MM",
"wind_gusts": 135.0,
"max_sustained_wind": 110.0,
"min_mslp": null,
"movement_direction": 315.0,
"movement_speed": 6.0,
"wind_extent_radii": {
"34kts": {
"wind_speed": 34.0,
"ne": 190.0,
"se": 210.0,
"nw": 180.0,
"sw": 150.0
},
"50kts": {
"wind_speed": 50.0,
"ne": 80.0,
"se": 80.0,
"nw": 70.0,
"sw": 90.0
},
"64kts": {
"wind_speed": 64.0,
"ne": 40.0,
"se": 40.0,
"nw": 30.0,
"sw": 30.0
}
},
"forecast_hour": 12,
"forecast_time": "2023-07-26T06:00:00+00:00"
}
]
}
]Response Fields — Storm Summary¶
| Field | Type | Description |
|---|---|---|
atcf_id | string | ATCF ID storm identifier |
storm_name | string | Storm name (empty until named) |
basin | string | Ocean basin code |
subbasin | string | Sub-basin code |
season | integer | Year of the storm season |
is_active | boolean | Whether storm is currently active |
advisory_number | integer | Advisory number (null for inactive storms) |
advisory_issuance_time | string | Advisory issuance time (null for inactive storms) |
max_development_category | string | Highest category reached over observed track |
max_sustained_wind | number | Maximum sustained wind (knots) over observed track |
min_mslp | number | Minimum sea-level pressure (hPa) over observed track |
dates_active | object | Start/end dates for all positions |
Response Fields — Track Positions¶
| Field | Type | Description |
|---|---|---|
lat | number | Latitude of storm center |
lon | number | Longitude of storm center |
category_code | string | Storm category at this position |
basin | string | Basin at this position |
subbasin | string | Sub-basin at this position |
wind_gusts | number | Maximum wind gusts (knots) |
max_sustained_wind | number | Maximum sustained wind (knots) |
min_mslp | number | Minimum sea-level pressure (hPa) |
movement_direction | number | Direction storm is moving toward (degrees from N) |
movement_speed | number | Speed of storm movement (knots) |
wind_extent_radii | object | Wind Radii for 34, 50, 64 kt thresholds (NE/SE/NW/SW quadrants, nautical miles) |
time | string | Valid time (observed track only) |
forecast_time | string | Valid time (forecast track only) |
forecast_hour | integer | Hours from advisory issuance (forecast track only) |
Storm KMZ¶
GET /storm/kmzRetrieve storm data as KMZ file for visualization in Google Earth Pro.
Parameters¶
Same parameters as /storm/json.
Example Request¶
curl -OJ -X GET \
'https://api.wx.spire.com/storm/kmz?name=Beryl&status=all' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/storm/kmz",
params={"name": "Beryl", "status": "all"},
headers={"spire-api-key": "YOUR_API_KEY"},
)
with open("Beryl.kmz", "wb") as f:
f.write(response.content)import { writeFile } from "fs/promises";
const response = await fetch(
"https://api.wx.spire.com/storm/kmz?name=Beryl&status=all",
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const buffer = Buffer.from(await response.arrayBuffer());
await writeFile("Beryl.kmz", buffer);Python Example¶
Track active storms and monitor intensity:
import requests
headers = {'spire-api-key': 'YOUR_API_KEY'}
# Get all active storms
response = requests.get(
'https://api.wx.spire.com/storm/json',
headers=headers,
params={'status': 'active'}
)
storms = response.json()
for storm in storms:
print(f"\n{storm['storm_name']} ({storm['atcf_id']})")
print(f" Basin: {storm['basin']}")
print(f" Category: {storm['max_development_category']}")
print(f" Max Wind: {storm['max_sustained_wind']} kt")
print(f" Min Pressure: {storm['min_mslp']} hPa")
print(f" Active: {storm['is_active']}")
# Get latest observed position
if storm['observed_track']:
latest = storm['observed_track'][-1]
print(f" Latest Position: {latest['lat']:.1f}N, {latest['lon']:.1f}E")
print(f" As of: {latest['time']}")
# Show forecast positions
if storm.get('forecast_track'):
print(f" Forecast positions: {len(storm['forecast_track'])}")
for pos in storm['forecast_track'][:3]:
print(f" +{pos['forecast_hour']}h: {pos['lat']:.1f}N, {pos['lon']:.1f}E "
f"({pos['category_code']}, {pos['max_sustained_wind']} kt)")Plot Storm Tracks¶
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
# Get historical storms for Atlantic 2024 season
response = requests.get(
'https://api.wx.spire.com/storm/json',
headers=headers,
params={
'region': 'NA',
'period': '2024-06-01T00:00:00/2024-11-30T00:00:00',
'status': 'inactive'
}
)
storms = response.json()
fig, ax = plt.subplots(
figsize=(12, 8),
subplot_kw={'projection': ccrs.PlateCarree()}
)
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.COASTLINE)
ax.set_extent([-100, -10, 5, 50])
for storm in storms:
# Plot observed track
obs = storm.get('observed_track', [])
if obs:
lats = [p['lat'] for p in obs]
lons = [p['lon'] for p in obs]
ax.plot(lons, lats, label=storm['storm_name'], transform=ccrs.PlateCarree())
ax.legend(loc='upper left')
plt.title('Atlantic Tropical Cyclones 2024')
plt.show()