Sign in, create an API token (there is a free tier!), and make a first request. Copy‑paste any of the examples below (cURL / Python). Swap tokens & timestamps and you’re live. Replace wherever you see [TOKEN] with your token (without the brackets!).
Query hourly temperature data for May 1st 2025 at Times Square according to NOAA's Global Forecast System (GFS).
We'll be requesting to this endpoint:
POST https://gribstream.com/api/v2/[MODEL]/history
So replace [MODEL]
with the code for GFS which is gfs
so it looks like this:
POST https://gribstream.com/api/v2/gfs/history
You can check all available models here
Timestamps are in ISO 8601 UTC format and we will be requesting a time range, from fromTime
to untilTime
.
Coordinates are (latitude, longitude) floating point pairs
Weather parameters are selected by (name, level, info) truples. name
and level
are mandatory but info
is only required when models provide multiple options for the same dimension and level. Looking at GFS, you'll see temperature is available with the TMP
name at many levels. Let's use 2m above ground
for this example.
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H [API_TOKEN] \
-d '{
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"coordinates": [{ "lat": 40.758, "lon": -73.985 }],
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "" }]
}'
import requests
API_TOKEN = "[API_TOKEN]" # Replace with your token
payload = {
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"coordinates": [
{ "lat": 40.758, "lon": -73.985 }
],
"variables": [
{ "name": "TMP", "level": "2 m above ground", "info": "" }
]
}
headers = {
"Authorization": f"Bearer {API_TOKEN}",
}
response = requests.post("https://gribstream.com/api/v2/nbm/history", json=payload, headers=headers)
response.raise_for_status()
print(response.text)
forecasted_at,forecasted_time,lat,lon,name,TMP|2 m above ground|
2025-05-01T06:00:00Z,2025-05-01T09:00:00Z,40.7580,-73.9850,,286.7000
2025-05-01T06:00:00Z,2025-05-01T11:00:00Z,40.7580,-73.9850,,286.5539
2025-05-01T00:00:00Z,2025-05-01T01:00:00Z,40.7580,-73.9850,,292.2532
2025-05-01T12:00:00Z,2025-05-01T15:00:00Z,40.7580,-73.9850,,292.2911
2025-05-01T18:00:00Z,2025-05-01T19:00:00Z,40.7580,-73.9850,,292.7923
2025-05-01T12:00:00Z,2025-05-01T14:00:00Z,40.7580,-73.9850,,290.9911
2025-05-01T12:00:00Z,2025-05-01T17:00:00Z,40.7580,-73.9850,,293.8677
2025-05-01T12:00:00Z,2025-05-01T16:00:00Z,40.7580,-73.9850,,293.2000
2025-05-01T18:00:00Z,2025-05-01T20:00:00Z,40.7580,-73.9850,,292.3354
2025-05-01T18:00:00Z,2025-05-01T18:00:00Z,40.7580,-73.9850,,293.5384
2025-05-01T06:00:00Z,2025-05-01T08:00:00Z,40.7580,-73.9850,,287.1673
2025-05-01T12:00:00Z,2025-05-01T13:00:00Z,40.7580,-73.9850,,289.4673
2025-05-01T00:00:00Z,2025-05-01T04:00:00Z,40.7580,-73.9850,,289.4754
2025-05-01T00:00:00Z,2025-05-01T03:00:00Z,40.7580,-73.9850,,290.4711
2025-05-01T00:00:00Z,2025-05-01T05:00:00Z,40.7580,-73.9850,,288.8000
2025-05-01T00:00:00Z,2025-05-01T00:00:00Z,40.7580,-73.9850,,292.9684
2025-05-01T12:00:00Z,2025-05-01T12:00:00Z,40.7580,-73.9850,,287.7405
2025-05-01T00:00:00Z,2025-05-01T02:00:00Z,40.7580,-73.9850,,291.3480
2025-05-01T06:00:00Z,2025-05-01T06:00:00Z,40.7580,-73.9850,,288.2327
2025-05-01T06:00:00Z,2025-05-01T10:00:00Z,40.7580,-73.9850,,286.2595
2025-05-01T18:00:00Z,2025-05-01T22:00:00Z,40.7580,-73.9850,,290.9539
2025-05-01T06:00:00Z,2025-05-01T07:00:00Z,40.7580,-73.9850,,287.7000
2025-05-01T18:00:00Z,2025-05-01T23:00:00Z,40.7580,-73.9850,,290.0113
2025-05-01T18:00:00Z,2025-05-01T21:00:00Z,40.7580,-73.9850,,291.8027
Notice the rows are not in forecasted_at nor forecasted_time order. This is for API efficiency.
When parsing the response it can be very convenient to use names instead of the coordinate pair. You can use it to give the location a human readable name or to store an ID. This is useful to create visualizations and dashboards with no pre-processing. You
achieve this by adding an extra name
property to each coordinate. In this case we'll use TimesSquare
Similarly, the three part naming convention for weather variables can be ugly to human eyes (TMP|2 m above ground|
🤮) so you can rename the variable with an extra property called alias
. It can be useful to programatically switch
between temperatures at different levels but still call the variable just temp
. Think of it as the keyword "as" in SQL.
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H "Authorization: Bearer [API_TOKEN] \
-d '{
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"coordinates": [{ "lat": 40.758, "lon": -73.985 , "name": "TimesSquare"}],
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }]
}'
import requests
API_TOKEN = "[API_TOKEN]" # Replace with your token
payload = {
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"coordinates": [
{ "lat": 40.758, "lon": -73.985, "name": "TimesSquare" }
],
"variables": [
{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }
]
}
headers = {
"Authorization": f"Bearer {API_TOKEN}",
}
response = requests.post("https://gribstream.com/api/v2/nbm/history", json=payload, headers=headers)
response.raise_for_status()
print(response.text)
forecasted_at,forecasted_time,lat,lon,name,temp
2025-05-01T05:00:00Z,2025-05-01T06:00:00Z,40.7580,-73.9850,TimesSquare,287.7400
2025-05-01T10:00:00Z,2025-05-01T11:00:00Z,40.7580,-73.9850,TimesSquare,285.7200
2025-05-01T03:00:00Z,2025-05-01T04:00:00Z,40.7580,-73.9850,TimesSquare,289.1600
2025-05-01T08:00:00Z,2025-05-01T09:00:00Z,40.7580,-73.9850,TimesSquare,285.7200
2025-05-01T20:00:00Z,2025-05-01T21:00:00Z,40.7580,-73.9850,TimesSquare,291.7000
2025-05-01T04:00:00Z,2025-05-01T05:00:00Z,40.7580,-73.9850,TimesSquare,288.4300
2025-05-01T02:00:00Z,2025-05-01T03:00:00Z,40.7580,-73.9850,TimesSquare,290.1200
2025-05-01T09:00:00Z,2025-05-01T10:00:00Z,40.7580,-73.9850,TimesSquare,285.4300
2025-05-01T06:00:00Z,2025-05-01T07:00:00Z,40.7580,-73.9850,TimesSquare,286.8900
2025-05-01T19:00:00Z,2025-05-01T20:00:00Z,40.7580,-73.9850,TimesSquare,292.5500
2025-05-01T07:00:00Z,2025-05-01T08:00:00Z,40.7580,-73.9850,TimesSquare,286.3500
2025-05-01T15:00:00Z,2025-05-01T16:00:00Z,40.7580,-73.9850,TimesSquare,292.9300
2025-05-01T11:00:00Z,2025-05-01T12:00:00Z,40.7580,-73.9850,TimesSquare,286.8000
2025-05-01T18:00:00Z,2025-05-01T19:00:00Z,40.7580,-73.9850,TimesSquare,293.4100
2025-05-01T17:00:00Z,2025-05-01T18:00:00Z,40.7580,-73.9850,TimesSquare,293.8500
2025-05-01T12:00:00Z,2025-05-01T13:00:00Z,40.7580,-73.9850,TimesSquare,288.1300
2025-05-01T21:00:00Z,2025-05-01T22:00:00Z,40.7580,-73.9850,TimesSquare,291.0500
2025-05-01T13:00:00Z,2025-05-01T14:00:00Z,40.7580,-73.9850,TimesSquare,290.2100
2025-05-01T14:00:00Z,2025-05-01T15:00:00Z,40.7580,-73.9850,TimesSquare,291.3900
2025-05-01T01:00:00Z,2025-05-01T02:00:00Z,40.7580,-73.9850,TimesSquare,291.2300
2025-05-01T00:00:00Z,2025-05-01T01:00:00Z,40.7580,-73.9850,TimesSquare,291.6000
2025-05-01T16:00:00Z,2025-05-01T17:00:00Z,40.7580,-73.9850,TimesSquare,293.6300
2025-04-30T23:00:00Z,2025-05-01T00:00:00Z,40.7580,-73.9850,TimesSquare,292.6500
2025-05-01T22:00:00Z,2025-05-01T23:00:00Z,40.7580,-73.9850,TimesSquare,289.6300
Pretty, eh?.
The format of the response is determined by the Accept
header. The API supports three response formats:
Accept: text/csv
- the response is a CSV file containing the forecast data.Accept: application/json
- each line in the response is a separate JSON object.Accept: application/ndjson
- the response is a single JSON array of objects.If not format is specified, the response format defaults to text/csv
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H "Authorization: Bearer [API_TOKEN] \
-H "Accept: text/csv" \
-d '{
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"coordinates": [{ "lat": 40.758, "lon": -73.985 , "name": "TimesSquare"}],
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }]
}'
forecasted_at,forecasted_time,lat,lon,name,temp
2025-05-01T05:00:00Z,2025-05-01T06:00:00Z,40.7580,-73.9850,TimesSquare,287.7400
2025-05-01T10:00:00Z,2025-05-01T11:00:00Z,40.7580,-73.9850,TimesSquare,285.7200
2025-05-01T03:00:00Z,2025-05-01T04:00:00Z,40.7580,-73.9850,TimesSquare,289.1600
2025-05-01T08:00:00Z,2025-05-01T09:00:00Z,40.7580,-73.9850,TimesSquare,285.7200
2025-05-01T20:00:00Z,2025-05-01T21:00:00Z,40.7580,-73.9850,TimesSquare,291.7000
2025-05-01T04:00:00Z,2025-05-01T05:00:00Z,40.7580,-73.9850,TimesSquare,288.4300
2025-05-01T02:00:00Z,2025-05-01T03:00:00Z,40.7580,-73.9850,TimesSquare,290.1200
2025-05-01T09:00:00Z,2025-05-01T10:00:00Z,40.7580,-73.9850,TimesSquare,285.4300
2025-05-01T06:00:00Z,2025-05-01T07:00:00Z,40.7580,-73.9850,TimesSquare,286.8900
2025-05-01T19:00:00Z,2025-05-01T20:00:00Z,40.7580,-73.9850,TimesSquare,292.5500
2025-05-01T07:00:00Z,2025-05-01T08:00:00Z,40.7580,-73.9850,TimesSquare,286.3500
2025-05-01T15:00:00Z,2025-05-01T16:00:00Z,40.7580,-73.9850,TimesSquare,292.9300
2025-05-01T11:00:00Z,2025-05-01T12:00:00Z,40.7580,-73.9850,TimesSquare,286.8000
2025-05-01T18:00:00Z,2025-05-01T19:00:00Z,40.7580,-73.9850,TimesSquare,293.4100
2025-05-01T17:00:00Z,2025-05-01T18:00:00Z,40.7580,-73.9850,TimesSquare,293.8500
2025-05-01T12:00:00Z,2025-05-01T13:00:00Z,40.7580,-73.9850,TimesSquare,288.1300
2025-05-01T21:00:00Z,2025-05-01T22:00:00Z,40.7580,-73.9850,TimesSquare,291.0500
2025-05-01T13:00:00Z,2025-05-01T14:00:00Z,40.7580,-73.9850,TimesSquare,290.2100
2025-05-01T14:00:00Z,2025-05-01T15:00:00Z,40.7580,-73.9850,TimesSquare,291.3900
2025-05-01T01:00:00Z,2025-05-01T02:00:00Z,40.7580,-73.9850,TimesSquare,291.2300
2025-05-01T00:00:00Z,2025-05-01T01:00:00Z,40.7580,-73.9850,TimesSquare,291.6000
2025-05-01T16:00:00Z,2025-05-01T17:00:00Z,40.7580,-73.9850,TimesSquare,293.6300
2025-04-30T23:00:00Z,2025-05-01T00:00:00Z,40.7580,-73.9850,TimesSquare,292.6500
2025-05-01T22:00:00Z,2025-05-01T23:00:00Z,40.7580,-73.9850,TimesSquare,289.6300
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H "Authorization: Bearer [API_TOKEN] \
-H "Accept: application/json" \
-d '{
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"coordinates": [{ "lat": 40.758, "lon": -73.985 , "name": "TimesSquare"}],
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }]
}'
[{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T00:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.96841796875}
,{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T11:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":286.553857421875}
,{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T05:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":288.80002441406253}
,{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T04:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":289.4754296875}
,{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T17:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":293.86765136718753}
,{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T13:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":289.46726074218753}
,{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T14:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.99106445312503}
,{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T19:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.7922607421875}
,{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T18:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":293.53837890625005}
,{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T20:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.33542480468753}
,{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T15:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.29106445312505}
,{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T21:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":291.802685546875}
,{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T09:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":286.70000000000005}
,{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T06:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":288.23271484375}
,{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T07:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":287.70000000000005}
,{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T22:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.953857421875}
,{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T08:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":287.1672607421875}
,{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T10:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":286.2595458984375}
,{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T12:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":287.74045410156253}
,{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T16:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":293.20000000000005}
,{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T02:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":291.348046875}
,{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T03:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.47107421875}
,{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T01:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.25318359375}
,{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T23:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.0113037109375}
]
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H "Authorization: Bearer [API_TOKEN] \
-H "Accept: application/ndjson" \
-d '{
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"coordinates": [{ "lat": 40.758, "lon": -73.985 , "name": "TimesSquare"}],
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }]
}'
{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T12:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":287.74045410156253}
{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T05:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":288.80002441406253}
{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T15:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.29106445312505}
{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T06:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":288.23271484375}
{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T16:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":293.20000000000005}
{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T22:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.953857421875}
{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T01:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.25318359375}
{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T02:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":291.348046875}
{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T03:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.47107421875}
{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T13:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":289.46726074218753}
{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T04:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":289.4754296875}
{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T14:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.99106445312503}
{"forecasted_at":"2025-05-01T00:00:00Z","forecasted_time":"2025-05-01T00:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.96841796875}
{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T07:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":287.70000000000005}
{"forecasted_at":"2025-05-01T12:00:00Z","forecasted_time":"2025-05-01T17:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":293.86765136718753}
{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T08:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":287.1672607421875}
{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T18:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":293.53837890625005}
{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T09:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":286.70000000000005}
{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T19:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.7922607421875}
{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T10:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":286.2595458984375}
{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T20:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":292.33542480468753}
{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T21:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":291.802685546875}
{"forecasted_at":"2025-05-01T06:00:00Z","forecasted_time":"2025-05-01T11:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":286.553857421875}
{"forecasted_at":"2025-05-01T18:00:00Z","forecasted_time":"2025-05-01T23:00:00Z","lat":40.758,"lon":-73.985,"name":"TimesSquare","temp":290.0113037109375}
Sometimes it can be useful to select a list of specific times instead of a range of time. For that you can use the timesList
argument in the request with each specific hour expected in the response.
For example, query the weather during New Year 🎉🥳🕛🍾🥂 2022, 2023, 2024 and 2025
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H "Authorization: Bearer [API_TOKEN]" \
-d '{
"timesList": [
"2022-01-01T00:00:00Z",
"2023-01-01T00:00:00Z",
"2024-01-01T00:00:00Z",
"2025-01-01T00:00:00Z"
],
"coordinates": [{ "lat": 40.758, "lon": -73.985 , "name": "TimesSquare"}],
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }]
}'
forecasted_at,forecasted_time,lat,lon,name,temp
2022-01-01T00:00:00Z,2022-01-01T00:00:00Z,40.7580,-73.9850,TimesSquare,283.1841
2024-01-01T00:00:00Z,2024-01-01T00:00:00Z,40.7580,-73.9850,TimesSquare,279.0649
2023-01-01T00:00:00Z,2023-01-01T00:00:00Z,40.7580,-73.9850,TimesSquare,281.7180
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,40.7580,-73.9850,TimesSquare,281.8039
Instead of querying data for specific coordinates you can define a grid of minLatitude
, maxLatitude
, minLongitude
, maxLongitude
, step
where step is the resolution in degrees.
Here is a nice visual example with our demo of wind fields
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H "Authorization: Bearer [API_TOKEN]" \
-d '{
"timesList": [ "2025-01-01T00:00:00Z" ],
"grid": {
"minLatitude": 25.00,
"maxLatitude": 27.00,
"minLongitude": -122.00,
"maxLongitude": -120.00,
"step": 0.5
},
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }]
}'
forecasted_at,forecasted_time,lat,lon,name,temp
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,27.0000,-122.0000,,290.0439
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,27.0000,-121.5000,,289.8739
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,27.0000,-121.0000,,289.7339
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,27.0000,-120.5000,,289.6239
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,27.0000,-120.0000,,289.5039
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.5000,-122.0000,,290.2839
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.5000,-121.5000,,290.1139
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.5000,-121.0000,,289.9739
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.5000,-120.5000,,289.8339
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.5000,-120.0000,,289.7239
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.0000,-122.0000,,290.5739
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.0000,-121.5000,,290.4239
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.0000,-121.0000,,290.2439
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.0000,-120.5000,,290.0339
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,26.0000,-120.0000,,289.8339
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.5000,-122.0000,,290.9539
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.5000,-121.5000,,290.7239
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.5000,-121.0000,,290.4739
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.5000,-120.5000,,290.2339
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.5000,-120.0000,,290.0439
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.0000,-122.0000,,291.1239
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.0000,-121.5000,,290.9539
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.0000,-121.0000,,290.7139
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.0000,-120.5000,,290.4539
2025-01-01T00:00:00Z,2025-01-01T00:00:00Z,25.0000,-120.0000,,290.2239
The forecast horizon is the time difference (in hours) between when a forecast was issued (forecasted_at
) and the time it predicts (forecasted_time
).
Example: To see only values that were predicted at least 12 hours in advance:
curl -X POST 'https://gribstream.com/api/v2/nbm/history' \
-H "Authorization: Bearer [API_TOKEN]" \
-d '{
"fromTime": "2025-05-01T12:00:00Z",
"untilTime": "2025-05-01T18:00:00Z",
"minHorizon": 12,
"coordinates": [{ "lat": 40.758, "lon": -73.985, "name": "TimesSquare" }],
"variables": [{ "name": "TMP", "level": "2 m above ground", "alias": "temp" }]
}'
forecasted_at,forecasted_time,lat,lon,name,temp,horizon_hours
2025-05-01T00:00:00Z,2025-05-01T12:00:00Z,40.7580,-73.9850,TimesSquare,286.6700
2025-05-01T01:00:00Z,2025-05-01T13:00:00Z,40.7580,-73.9850,TimesSquare,288.0300
2025-05-01T02:00:00Z,2025-05-01T14:00:00Z,40.7580,-73.9850,TimesSquare,289.5900
2025-05-01T03:00:00Z,2025-05-01T15:00:00Z,40.7580,-73.9850,TimesSquare,290.9600
2025-05-01T04:00:00Z,2025-05-01T16:00:00Z,40.7580,-73.9850,TimesSquare,292.3300
2025-05-01T05:00:00Z,2025-05-01T17:00:00Z,40.7580,-73.9850,TimesSquare,293.1900
Note: Rows are presented here sorted by forecasted_at
to make the result easier to grok.
Another example: To only include short-range predictions up to 3 hours out:
curl -X POST 'https://gribstream.com/api/v2/nbm/history' \
-H "Authorization: Bearer [API_TOKEN]" \
-d '{
"fromTime": "2025-05-01T12:00:00Z",
"untilTime": "2025-05-01T15:00:00Z",
"maxHorizon": 3,
"coordinates": [{ "lat": 40.758, "lon": -73.985 }],
"variables": [{ "name": "TMP", "level": "2 m above ground" }]
}'
Use min_horizon and max_horizon together to restrict the forecasted values to any desired prediction window.
Tip: Unlike the asOf
parameter (see next example), which restricts results to only data available before a certain time, min_horizon
and max_horizon
filter by the age of the forecast relative to the
predicted value.
When backtesting predictive models, it's essential to query data as it was known at a specific point in time, not after the fact.
For example: “What was the forecast for May 1st, based on what was available by April 30th at 12:00 UTC?”
This is called a time-travel query: it returns only the data that was available as of the specified moment in the past, not any newer corrections or updates.
curl -X POST 'https://gribstream.com/api/v2/nbm/history' \
-H "Authorization: Bearer [API_TOKEN]" \
-d '{
"fromTime": "2025-05-01T00:00:00Z",
"untilTime": "2025-05-02T00:00:00Z",
"asOf": "2025-05-01T12:00:00Z",
"coordinates": [{ "lat": 40.758, "lon": -73.985 , "name": "TimesSquare"}],
"variables": [{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" }]
}'
forecasted_at,forecasted_time,lat,lon,name,temp
2025-04-30T23:00:00Z,2025-05-01T00:00:00Z,40.7580,-73.9850,TimesSquare,292.6500
2025-05-01T00:00:00Z,2025-05-01T01:00:00Z,40.7580,-73.9850,TimesSquare,291.6000
2025-05-01T01:00:00Z,2025-05-01T02:00:00Z,40.7580,-73.9850,TimesSquare,291.2300
2025-05-01T02:00:00Z,2025-05-01T03:00:00Z,40.7580,-73.9850,TimesSquare,290.1200
2025-05-01T03:00:00Z,2025-05-01T04:00:00Z,40.7580,-73.9850,TimesSquare,289.1600
2025-05-01T04:00:00Z,2025-05-01T05:00:00Z,40.7580,-73.9850,TimesSquare,288.4300
2025-05-01T05:00:00Z,2025-05-01T06:00:00Z,40.7580,-73.9850,TimesSquare,287.7400
2025-05-01T06:00:00Z,2025-05-01T07:00:00Z,40.7580,-73.9850,TimesSquare,286.8900
2025-05-01T07:00:00Z,2025-05-01T08:00:00Z,40.7580,-73.9850,TimesSquare,286.3500
2025-05-01T08:00:00Z,2025-05-01T09:00:00Z,40.7580,-73.9850,TimesSquare,285.7200
2025-05-01T09:00:00Z,2025-05-01T10:00:00Z,40.7580,-73.9850,TimesSquare,285.4300
2025-05-01T10:00:00Z,2025-05-01T11:00:00Z,40.7580,-73.9850,TimesSquare,285.7200
2025-05-01T11:00:00Z,2025-05-01T12:00:00Z,40.7580,-73.9850,TimesSquare,286.8000
2025-05-01T12:00:00Z,2025-05-01T13:00:00Z,40.7580,-73.9850,TimesSquare,288.1300
2025-05-01T12:00:00Z,2025-05-01T14:00:00Z,40.7580,-73.9850,TimesSquare,290.1600
2025-05-01T12:00:00Z,2025-05-01T15:00:00Z,40.7580,-73.9850,TimesSquare,291.3400
2025-05-01T12:00:00Z,2025-05-01T16:00:00Z,40.7580,-73.9850,TimesSquare,292.6500
2025-05-01T12:00:00Z,2025-05-01T17:00:00Z,40.7580,-73.9850,TimesSquare,293.0100
2025-05-01T12:00:00Z,2025-05-01T18:00:00Z,40.7580,-73.9850,TimesSquare,293.1500
2025-05-01T12:00:00Z,2025-05-01T19:00:00Z,40.7580,-73.9850,TimesSquare,292.7500
2025-05-01T12:00:00Z,2025-05-01T20:00:00Z,40.7580,-73.9850,TimesSquare,292.1500
2025-05-01T12:00:00Z,2025-05-01T21:00:00Z,40.7580,-73.9850,TimesSquare,291.3500
2025-05-01T12:00:00Z,2025-05-01T22:00:00Z,40.7580,-73.9850,TimesSquare,290.7100
2025-05-01T12:00:00Z,2025-05-01T23:00:00Z,40.7580,-73.9850,TimesSquare,289.5800
Note: Rows are presented here sorted by forecasted_at
to make the result easier to grok.
This example uses the NOAA National Blend of Models (NBM), which runs hourly. Notice that forecasted_at
is always the hour previous to forecasted_time
, meaning the best forecast for that
hour, except for times after asOf
which is set to 2025-05-01T12:00:00Z
. Model runs after that time are excluded.
GribStream lets you define expressions to calculate derived columns, transformations, and boolean filters in your API queries.
Expressions use the expr language, supporting numeric, logical, and string operations with access to math functions.
To see a really cool demo dashboard doing a single API request see: Corn growth simulation
func.Hypot(uwind, vwind)
int(270 - func.Atan2(vwind, uwind) * 180 / 3.14159) % 360
func.Hypot(uwind, vwind) > 10
curl -X POST 'https://gribstream.com/api/v2/hrrr/history' \
-H "Content-Type: application/json" \
-H "Authorization: [API_TOKEN]" \
-d '{
"fromTime": "2024-09-10T00:00:00Z",
"untilTime": "2024-09-10T10:00:00Z",
"coordinates": [{ "lat": 40.7306, "lon": -73.9352, "name": "New York City" }],
"variables": [
{ "name": "UGRD", "level": "1000 mb", "alias": "uwind" },
{ "name": "VGRD", "level": "1000 mb", "alias": "vwind" }
],
"expressions": [
{ "expression": "func.Hypot(uwind, vwind)", "alias": "wind_magnitude" },
{ "expression": "int(270 - func.Atan2(vwind, uwind) * 180 / 3.14159) % 360", "alias": "wind_direction" }
]
}'
forecasted_at,forecasted_time,lat,lon,name,uwind,vwind,wind_direction,wind_magnitude
2024-09-10T02:00:00Z,2024-09-10T03:00:00Z,40.7306,-73.9352,New York City,7.1213,1.7680,256,7.3375
2024-09-10T01:00:00Z,2024-09-10T02:00:00Z,40.7306,-73.9352,New York City,8.1815,1.6363,258,8.3435
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,40.7306,-73.9352,New York City,8.0572,2.9389,249,8.5765
2024-09-10T05:00:00Z,2024-09-10T06:00:00Z,40.7306,-73.9352,New York City,6.8368,0.1001,269,6.8375
2024-09-10T04:00:00Z,2024-09-10T05:00:00Z,40.7306,-73.9352,New York City,6.4087,3.0921,244,7.1157
2024-09-10T03:00:00Z,2024-09-10T04:00:00Z,40.7306,-73.9352,New York City,6.4841,2.5385,248,6.9633
2024-09-09T23:00:00Z,2024-09-10T00:00:00Z,40.7306,-73.9352,New York City,6.1795,3.9539,237,7.3362
2024-09-10T08:00:00Z,2024-09-10T09:00:00Z,40.7306,-73.9352,New York City,5.9424,-0.6889,276,5.9822
2024-09-10T06:00:00Z,2024-09-10T07:00:00Z,40.7306,-73.9352,New York City,7.3567,1.5594,258,7.5201
2024-09-10T07:00:00Z,2024-09-10T08:00:00Z,40.7306,-73.9352,New York City,6.5438,0.7486,263,6.5864
GribStream lets you define expressions to filter in space and time, only returning data matching the condition.
Filtering expressions follow the same syntax of calculated expressions but they must evaluate to true
or false
.
To see a really cool demo dashboard doing a single API request see: Storm chasers
curl -X POST 'https://gribstream.com/api/v2/gfs/history' \
-H "Content-Type: application/json" \
-H "Authorization: [API_TOKEN] \
-d '{
"fromTime": "2024-09-10T00:00:00Z",
"untilTime": "2024-09-10T10:00:00Z",
"grid": {
"minLatitude": 24.52,
"maxLatitude": 49.38,
"minLongitude": -124.77,
"maxLongitude": -66.93,
"step": 0.5
},
"variables": [
{"name": "CAPE", "level": "180-0 mb above ground", "info": "", "alias": "cape"},
{"name": "CIN", "level": "180-0 mb above ground", "info": "", "alias": "cin"}
],
"expressions":[
{ "expression": "cape + cin", "alias": "storm_severity"}
],
"filter":{"expression": "storm_severity >= 1200"}
}'
forecasted_at,forecasted_time,lat,lon,name,cape,cin,offset_hours,storm_severity
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,29.0200,-70.7700,,1337.0000,-1.6324,-5713.0000,1335.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,29.0200,-70.2700,,1204.0000,-1.6324,-5713.0000,1202.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.5200,-71.7700,,1432.0000,-0.6324,-5713.0000,1431.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.5200,-71.2700,,1384.0000,-1.6324,-5713.0000,1382.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.5200,-70.7700,,1355.0000,-0.6324,-5713.0000,1354.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.5200,-70.2700,,1388.0000,-1.6324,-5713.0000,1386.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.0200,-112.2700,,1599.0000,-148.6324,-5713.0000,1450.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.0200,-111.2700,,1424.0000,-104.6324,-5713.0000,1319.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.0200,-75.2700,,1328.0000,-0.6324,-5713.0000,1327.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.0200,-74.7700,,1242.0000,-1.6324,-5713.0000,1240.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.0200,-73.2700,,1327.0000,0.3676,-5713.0000,1327.3676
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,28.0200,-72.7700,,1471.0000,0.3676,-5713.0000,1471.3676
...
All previous examples use the history
endpoint. It's purpose is to return the "best" forecast possible for all times.
The best forecast is always the most recent one or said differently the one with the lowest horizon. The minHorizon
, maxHorizon
and asOf
parameters allow specifying restrictions
on what model runs are to be taken into consideration but for any forecasted value a single model run is chosen.
If what we are actually looking for is to retrieve all forecasts of each value then we need to leverage the forecasts
endpoint.
POST https://gribstream.com/api/v2/[MODEL]/forecasts
This endpoint chooses the time range based on parameters named forecastedFrom
and forecastedUntil
to make it clear that the data is being selected based on the time the forecast is made. Specifying model run times by
enumeration with the timesList
parameter is also an option and it is useful when looking for specific model run times.
All other parameters remain available and the same except asOf
that is not available. So you can filter by minHorizon
and maxHorizon
, define expressions with calculated values, filter by them, etc.
curl -X POST 'https://gribstream.com/api/v2/nbm/forecasts' \
-H "Authorization: [API_TOKEN]"" \
-d '{
"forecastedFrom": "2024-09-10T00:00:00Z",
"forecastedUntil": "2024-09-10T03:00:00Z",
"minHorizon": 1,
"maxHorizon": 3,
"coordinates": [
{ "lat": 47.6, "lon": -122.33 }
],
"variables": [
{ "name": "TMP", "level": "2 m above ground", "info": "", "alias": "temp" },
{ "name": "WIND", "level": "10 m above ground", "info": "", "alias": "wind_speed" },
{ "name": "DPT", "level": "2 m above ground", "info": "", "alias": "dew_point" }
]
}'
forecasted_at,forecasted_time,lat,lon,name,dew_point,temp,wind_speed
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,47.6000,-122.3300,,288.2500,294.2700,1.6000
2024-09-10T00:00:00Z,2024-09-10T02:00:00Z,47.6000,-122.3300,,288.4000,293.4800,1.6000
2024-09-10T00:00:00Z,2024-09-10T03:00:00Z,47.6000,-122.3300,,288.2600,293.0000,1.6000
2024-09-10T01:00:00Z,2024-09-10T02:00:00Z,47.6000,-122.3300,,288.3500,293.4900,1.6000
2024-09-10T01:00:00Z,2024-09-10T03:00:00Z,47.6000,-122.3300,,288.1800,292.9800,1.6000
2024-09-10T01:00:00Z,2024-09-10T04:00:00Z,47.6000,-122.3300,,288.1900,292.2100,1.2000
2024-09-10T02:00:00Z,2024-09-10T03:00:00Z,47.6000,-122.3300,,288.1600,292.9500,1.6000
2024-09-10T02:00:00Z,2024-09-10T04:00:00Z,47.6000,-122.3300,,288.3800,292.1800,1.2000
2024-09-10T02:00:00Z,2024-09-10T05:00:00Z,47.6000,-122.3300,,287.9300,291.2800,1.2000
2024-09-10T03:00:00Z,2024-09-10T04:00:00Z,47.6000,-122.3300,,288.3300,292.0300,1.2000
2024-09-10T03:00:00Z,2024-09-10T05:00:00Z,47.6000,-122.3300,,287.9400,291.4900,1.2000
2024-09-10T03:00:00Z,2024-09-10T06:00:00Z,47.6000,-122.3300,,287.8000,290.8100,1.2000
Note: Rows are presented here sorted by forecasted_at
to make the result easier to grok. Notice each model run time returned horizons 1 to 3
Please reach out for scenarios not covered in this guide. info@gribstream.com.