GET /weather/forecast
Returns forecast weather variables for one GPS point and one forecast target: 2 m temperature, precipitation and 10 m wind, served from GFS data decoded from GRIB into a local grid store. The runtime lookup is local, so a covered target resolves in milliseconds without an account or API key.
This is the operational counterpart to
GET /climate/point: use it when an agent needs a
future weather signal for energy dispatch, logistics routing, agricultural
planning or short-term risk checks. See the live /catalog
for the authoritative endpoint listing and price.
x402 golden rule: the agent pays for the answer to its question. A
well-formed and covered request is a successful answer -> 200, even when a
requested variable is null because the grid cell has a data gap. Requests the
service cannot answer - invalid coordinates, invalid target time, an uncovered
forecast horizon, an unknown variable or a missing forecast cycle - leave the
200 range.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
lat | number | yes | Latitude in decimal degrees, from -90 to 90 |
lon | number | yes | Longitude in decimal degrees, from -180 to 180 |
horizon_h | number | one of target fields | Forecast lead in hours from the current GFS cycle, integer 0 to 384 |
valid_time | string | one of target fields | Forecast target instant in RFC 3339 UTC form, for example 2026-06-20T12:00:00Z |
variables | string | no | Comma-separated subset of temperature, precipitation, wind; all by default |
Provide exactly one of horizon_h or valid_time.
GET /weather/forecast?lat=48.8566&lon=2.3522&horizon_h=24
GET /weather/forecast?lat=48.8566&lon=2.3522&valid_time=2026-06-20T12:00:00Z
Use variables to reduce the payload when the agent only needs one family:
GET /weather/forecast?lat=48.8566&lon=2.3522&horizon_h=24&variables=temperature,wind
200 response - UnifiedResponse
{
"data": { ... },
"provenance": {
"source": "gfs-noaa",
"fetched_at": "2026-06-20T12:00:00Z",
"freshness": { "kind": "live" }
}
}
provenance.source: source identifier from the served GFS store, commonlygfs-noaaorGFS.freshness.kind:livewhen the cycle is fresh, orcachedwithage_secswhen a stale but still covered cycle is served.- GFS data comes from NOAA and is public-domain U.S. government data.
Fields of data
| Field | Type | Description |
|---|---|---|
lat | number | Latitude exactly echoed from the request |
lon | number | Longitude exactly echoed from the request |
horizon_h | number | Echoed when the request used horizon_h; omitted otherwise |
valid_time | string | Echoed when the request used valid_time; omitted otherwise |
temperature | object | Requested 2 m temperature; omitted when not requested |
precipitation | object | Requested precipitation; omitted when not requested |
wind | object | Requested 10 m wind; omitted when not requested |
grid | object | Effective grid cell used for the answer; omitted if no cell served |
lead | object | Resolved cycle, target and served model step |
coverage | object | Completeness marker for the requested variables |
temperature
| Field | Type | Description |
|---|---|---|
celsius | number | null | 2 m temperature in Celsius |
precipitation
| Field | Type | Description |
|---|---|---|
total_mm | number | null | Forecast precipitation total in millimetres |
wind
| Field | Type | Description |
|---|---|---|
speed_ms | number | null | 10 m wind speed in metres per second |
direction_deg | number | null | Meteorological direction in degrees: 0 = north, 90 = east |
grid
| Field | Type | Description |
|---|---|---|
lat | number | Latitude of the representative grid cell |
lon | number | Longitude of the representative grid cell |
distance_km | number | Distance from the requested point to that cell |
lead
lead makes the resolved forecast target explicit, whether the caller supplied
horizon_h or valid_time.
| Field | Type | Description |
|---|---|---|
cycle | string | GFS analysis cycle used for the answer, RFC 3339 UTC |
valid_time | string | Resolved target instant, RFC 3339 UTC |
horizon_h | number | Lead time in hours from cycle to valid_time |
step_time | string | null | Actual model step served; omitted if no step was served |
coverage
coverage tells the agent whether every requested variable was actually
available at the served grid cell.
| Field | Type | Description |
|---|---|---|
complete | bool | true when all requested variables have values |
reason | string | Present when complete: false, naming the missing family data |
Example - horizon target
{
"data": {
"lat": 48.8566,
"lon": 2.3522,
"horizon_h": 24,
"temperature": { "celsius": 18.6 },
"precipitation": { "total_mm": 0.4 },
"wind": { "speed_ms": 6.1, "direction_deg": 242.3 },
"grid": { "lat": 48.75, "lon": 2.25, "distance_km": 13.42 },
"lead": {
"cycle": "2026-06-20T00:00:00Z",
"valid_time": "2026-06-21T00:00:00Z",
"horizon_h": 24,
"step_time": "2026-06-21T00:00:00Z"
},
"coverage": { "complete": true }
},
"provenance": {
"source": "gfs-noaa",
"fetched_at": "2026-06-20T01:20:00Z",
"freshness": { "kind": "live" }
}
}
Example - explicit valid_time
When the request uses valid_time, that field is echoed and horizon_h is
omitted from the top-level data. The resolved lead still carries the computed
horizon.
{
"data": {
"lat": 48.8566,
"lon": 2.3522,
"valid_time": "2026-06-21T00:00:00Z",
"temperature": { "celsius": 18.6 },
"precipitation": { "total_mm": 0.4 },
"wind": { "speed_ms": 6.1, "direction_deg": 242.3 },
"grid": { "lat": 48.75, "lon": 2.25, "distance_km": 13.42 },
"lead": {
"cycle": "2026-06-20T00:00:00Z",
"valid_time": "2026-06-21T00:00:00Z",
"horizon_h": 24,
"step_time": "2026-06-21T00:00:00Z"
},
"coverage": { "complete": true }
},
"provenance": {
"source": "gfs-noaa",
"fetched_at": "2026-06-20T01:20:00Z",
"freshness": { "kind": "live" }
}
}
Example - stale but covered cycle
A stale cycle can still answer a covered target. It remains a 200, with
freshness.kind = "cached" and an age in seconds so the agent can decide whether
to trust or retry later.
{
"data": {
"lat": 48.8566,
"lon": 2.3522,
"horizon_h": 3,
"temperature": { "celsius": 17.1 },
"precipitation": { "total_mm": 0.0 },
"wind": { "speed_ms": 4.2, "direction_deg": 190.0 },
"grid": { "lat": 48.75, "lon": 2.25, "distance_km": 13.42 },
"lead": {
"cycle": "2026-06-20T00:00:00Z",
"valid_time": "2026-06-20T03:00:00Z",
"horizon_h": 3,
"step_time": "2026-06-20T03:00:00Z"
},
"coverage": { "complete": true }
},
"provenance": {
"source": "gfs-noaa",
"fetched_at": "2026-06-20T09:30:00Z",
"freshness": { "kind": "cached", "age_secs": 34200 }
}
}
Coverage honesty
GFS is a gridded forecast model, not a weather station reading. The response is
the nearest or interpolated grid value for the served cell, with a typical
global mesh of about 0.25 degrees. Forecast steps are discrete, commonly around
3 hours, and the public horizon is bounded by the currently ingested forecast
window - about the next 4 to 5 days. (The GFS model itself extends to 384
hours; the ingested horizon will be widened toward it, and this page updated,
when that lands.)
A target inside the available forecast window can return 200 with null
values and coverage.complete: false when the grid has a local gap. A target
outside the available window returns 400 OUT_OF_RANGE, because the service
cannot answer the request. If no GFS cycle has been ingested yet, the service
returns 503 NO_FORECAST_AVAILABLE; that is a transient no-charge state, not
a paid partial answer.
Errors
Only requests the service cannot answer leave the 200 range.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_COORDS | lat/lon missing, non-numeric or outside valid bounds |
| 400 | INVALID_TIME | Missing target, both target fields present, malformed RFC 3339, or horizon_h outside 0..384 |
| 400 | OUT_OF_RANGE | Target is outside the available forecast window |
| 400 | INVALID_VARIABLE | variables contains a family outside the supported set |
| 503 | NO_FORECAST_AVAILABLE | No GFS forecast cycle is currently available in the store |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "one of valid_time or horizon_h is required", "code": "INVALID_TIME" }
{ "error": "requested lead time is outside the available forecast window 2026-06-20T00:00:00+00:00..2026-06-20T06:00:00+00:00", "code": "OUT_OF_RANGE" }
{ "error": "no GFS forecast cycle currently available", "code": "NO_FORECAST_AVAILABLE" }
See also
GET /climate/point- historical ERA5 reanalysis for dated climate evidence.- For agents - discovery surfaces, the live
/catalogand how settlement works.