Access historical weather data through Archive Data retrieval requests. Historical data is available dating back to January 1, 1990.
Overview¶
Archive data retrieval is an asynchronous process:
Submit an archive request
Check job status (job progresses through several stages)
Download files when ready
How It Works¶
Historical weather is Spire’s global reanalysis: hourly data at 1/8° from 1 January 1990 onwards, produced about six days behind real time. Requests that include the most recent six days are completed from Spire’s short-range historical forecasts (the 6-hourly analyses plus the first hours of each run); the output then contains one sub-folder per source, and issuance_time and valid_time can differ by up to five hours for those records.
Retrieval is asynchronous:
Submit the route. The API validates it, reserves the waypoints against your account’s waypoint quota and returns a
job_uuid. Keep it: you need it to check status and download the result.Poll the job status. It moves through the stages listed below. Every status response carries
export_uri, the export location; once the status iscompletedthe files are there, and the reserved waypoints are deducted from your quota. Waypoints of a failed job are returned to the quota. A one-waypoint test job completed in about two minutes.Download the output. List the export location: it returns one ZIP path of the form
{export_id}/{export_id}.zip. Append that path to the export endpoint to download. The ZIP contains one folder per data source with a{route name}.jsonor.csvfile inside.
Rules for the route:
Waypoint times are snapped to the nearest hour available in the archive (a request for 03:20 returns the 03:00 data).
The route name is used in the output file name and may contain only letters, digits, underscores and hyphens.
Up to 10,000 waypoints per request; the total across requests is limited by your quota.
Jobs cannot be cancelled through the API. If you submitted a wrong request, contact Spire as soon as possible. Jobs that have not finished after 72 hours should also be reported with their
job_uuid.
Endpoints¶
| Method | Endpoint | Description |
|---|---|---|
| POST | /archive/route | Request archive data along a route |
| GET | /archive/jobs/{job_uuid}/status | Check archive job status |
| GET | /export/{request_id} | List files from archive request |
| GET | /export/{request_id}/{file_id} | Download archive file |
Archive Route Request¶
POST /archive/routeRequest historical weather data along a route with timestamps.
Request Body¶
The request body uses the same route structure as the forecast route endpoint, with additional options for variable selection and output format.
{
"route": {
"name": "historical_voyage",
"waypoints": [
{
"lat": 40.0,
"lon": -74.0,
"time": "2023-06-15T12:00:00Z"
},
{
"lat": 41.0,
"lon": -70.0,
"time": "2023-06-15T18:00:00Z"
},
{
"lat": 42.0,
"lon": -66.0,
"time": "2023-06-16T00:00:00Z"
}
]
},
"fields": [
"air_temperature",
"wind_speed",
"wind_direction",
"sea_surface_wave_significant_height"
],
"output_format": "CSV"
}Request Body Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
route | object | Yes | Route definition |
route.name | string | No | Name used for the output file (default route_1); letters, digits, underscores and hyphens only |
route.waypoints | array | Yes | Array of waypoints (max 10,000) |
route.waypoints[].lat | number | Yes | Latitude |
route.waypoints[].lon | number | Yes | Longitude |
route.waypoints[].time | string | Yes | ISO 8601 timestamp |
fields | array | One of fields or bundles | Variable names to include |
bundles | array | One of fields or bundles | Archive bundle names: core, agricultural, maritime, maritime-wave, maritime-swell-wave, sea-surface, precipitation, wind-energy, solar-energy, thunderstorm |
output_format | string | No | JSON (default) or CSV |
Example Request¶
curl -X POST \
'https://api.wx.spire.com/archive/route' \
-H 'spire-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"route": {
"name": "historical_voyage",
"waypoints": [
{"lat": 40.0, "lon": -74.0, "time": "2023-06-15T12:00:00Z"},
{"lat": 42.0, "lon": -66.0, "time": "2023-06-16T00:00:00Z"}
]
},
"fields": ["air_temperature", "wind_speed", "sea_surface_wave_significant_height"],
"output_format": "CSV"
}'import requests
response = requests.post(
"https://api.wx.spire.com/archive/route",
headers={"spire-api-key": "YOUR_API_KEY"},
json={
"route": {
"name": "historical_voyage",
"waypoints": [
{"lat": 40.0, "lon": -74.0, "time": "2023-06-15T12:00:00Z"},
{"lat": 42.0, "lon": -66.0, "time": "2023-06-16T00:00:00Z"},
],
},
"fields": ["air_temperature", "wind_speed", "sea_surface_wave_significant_height"],
"output_format": "CSV",
},
)
data = response.json()const response = await fetch("https://api.wx.spire.com/archive/route", {
method: "POST",
headers: {
"spire-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
route: {
name: "historical_voyage",
waypoints: [
{ lat: 40.0, lon: -74.0, time: "2023-06-15T12:00:00Z" },
{ lat: 42.0, lon: -66.0, time: "2023-06-16T00:00:00Z" },
],
},
fields: ["air_temperature", "wind_speed", "sea_surface_wave_significant_height"],
output_format: "CSV",
}),
});
const data = await response.json();Response¶
{
"job_uuid": "3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10",
"job_type": "RouteRetrievalJob",
"job_status": "initiated",
"creation_time": "2026-09-21T14:44:45+00:00",
"export_uri": "https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10"
}Keep the job_uuid; the status and export endpoints take it as their path parameter. export_uri is the export location you will list once the job is completed.
Check Job Status¶
GET /archive/jobs/{job_uuid}/statusCheck the status of an archive data retrieval job.
Path Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
job_uuid | string | Yes | Job identifier from request |
Example Request¶
curl -X GET \
'https://api.wx.spire.com/archive/jobs/abc123-def456-789xyz/status' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/archive/jobs/abc123-def456-789xyz/status",
headers={"spire-api-key": "YOUR_API_KEY"},
)
data = response.json()const response = await fetch(
"https://api.wx.spire.com/archive/jobs/abc123-def456-789xyz/status",
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();Response¶
{
"job_uuid": "3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10",
"job_type": "RouteRetrievalJob",
"job_status": "completed",
"creation_time": "2026-09-21T14:44:45+00:00",
"export_uri": "https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10"
}Status Values¶
Archive jobs progress through the stages below. The API returns the status in lower case.
| Status | Description |
|---|---|
created | Job submitted |
queued | Waiting for a processing slot |
restoring_files | Retrieving archived data from storage |
restore_completed | Archived data retrieved, ready for extraction |
initiated | Extraction process initiated |
processing | Data extraction in progress |
completed | Data ready for download |
failed | Job failed |
timeout | Job exceeded its processing time limit |
unknown | Status could not be determined |
List Export Files¶
GET /export/{request_id}Retrieve a list of files available from a completed archive request. The request_id is the job_uuid (the last path segment of export_uri). Route jobs return one entry of the form {export_id}/{export_id}.zip. An unknown or not yet completed request_id returns an empty files list, so check meta.count before downloading.
Path Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id | string | Yes | Request identifier from completed job |
Example Request¶
curl -X GET \
'https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10",
headers={"spire-api-key": "YOUR_API_KEY"},
)
data = response.json()const response = await fetch(
"https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10",
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();Response¶
{
"meta": {
"count": 1
},
"files": [
"8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f/8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f.zip"
]
}The ZIP holds one folder per data source (the reanalysis, and the recent-forecast fill for the last six days when the route reaches into them) containing {route name}.json or .csv. A JSON record looks like this:
{
"data": [
{
"coordinates": {"lat": 52.0, "lon": 4.0},
"times": {
"valid_time": "2026-09-01T12:00:00",
"requested_valid_time": "2026-09-01T12:00:00"
},
"values": {"air_temperature": 291.5}
}
]
}Download Export File¶
GET /export/{request_id}/{file_id}Download a specific file from an archive export.
Path Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id | string | Yes | Request identifier |
file_id | string | Yes | Filename to download |
Example Request¶
curl -OJL -X GET \
'https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10/8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f/8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f.zip' \
-H 'spire-api-key: YOUR_API_KEY'import requests
response = requests.get(
"https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10/8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f/8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f.zip",
headers={"spire-api-key": "YOUR_API_KEY"},
allow_redirects=True,
)
with open("route_export.zip", "wb") as f:
f.write(response.content)import { createWriteStream } from "fs";
import { Readable } from "stream";
const response = await fetch(
"https://api.wx.spire.com/export/3f2b9c1e-7a4d-4e0b-9c1a-2d6f8e5b7a10/8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f/8c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f.zip",
{ headers: { "spire-api-key": "YOUR_API_KEY" } }
);
const fileStream = createWriteStream("route_export.zip");
Readable.fromWeb(response.body).pipe(fileStream);Python Example¶
Complete workflow for archive data retrieval:
import requests
import time
headers = {'spire-api-key': 'YOUR_API_KEY'}
base_url = 'https://api.wx.spire.com'
# 1. Submit archive request
route_data = {
"route": {
"name": "historical_voyage",
"waypoints": [
{"lat": 40.0, "lon": -74.0, "time": "2023-06-15T12:00:00Z"},
{"lat": 41.0, "lon": -70.0, "time": "2023-06-15T18:00:00Z"},
{"lat": 42.0, "lon": -66.0, "time": "2023-06-16T00:00:00Z"}
]
},
"fields": [
"air_temperature",
"wind_speed",
"wind_direction",
"sea_surface_wave_significant_height"
],
"output_format": "CSV"
}
response = requests.post(
f'{base_url}/archive/route',
headers=headers,
json=route_data
)
job = response.json()
job_uuid = job['job_uuid']
print(f"Job submitted: {job_uuid}")
# 2. Poll for completion
terminal_statuses = {'completed', 'failed', 'unknown'}
while True:
status_response = requests.get(
f'{base_url}/archive/jobs/{job_uuid}/status',
headers=headers
)
status = status_response.json()
print(f"Status: {status['job_status']}")
if status['job_status'].lower() in terminal_statuses:
break
time.sleep(30) # Wait before checking again
if status['job_status'].lower() != 'completed':
raise Exception(f"Archive job ended with status: {status['job_status']}")
export_uri = status['export_uri']
# 3. List and download files
files_response = requests.get(
export_uri,
headers=headers
)
files = files_response.json()['files']
for filename in files:
file_response = requests.get(
f'{export_uri}/{filename}',
headers=headers,
allow_redirects=True
)
with open(filename.split('/')[-1], 'wb') as f:
f.write(file_response.content)
print(f'Downloaded: {filename}')