POST /weather/forecast/batch
Resolves many point-and-target forecasts in a single call. Each item uses the
same local GRIB-decoded engine as
GET /weather/forecast: 2 m temperature,
precipitation and 10 m wind for one point and one forecast target, with grid,
lead and coverage metadata that let an agent judge each answer. It is the
many-points-at-once companion to the single endpoint - use it when an agent has a
portfolio of locations or lead times to score in one shot: multi-site energy
dispatch, logistics across a route, or a fan-out of horizons for one place.
The batch is settled as one x402 payment for the whole call and priced per
query by the gateway. See the live /catalog for the
authoritative endpoint listing, pricing model and batch cap.
x402 golden rule: the agent pays for the answer to its question. A
well-formed batch is a successful answer -> 200, even when individual items
are outside coverage and come back with null values and
coverage.complete: false. Requests the service cannot answer - malformed body,
empty batch, batch over the published cap, a malformed item, or a batch where
no item is covered at all - leave the 200 range.
Request
POST with a JSON body. Set Content-Type: application/json.
POST /weather/forecast/batch
Content-Type: application/json
{
"queries": [
{ "lat": 48.8566, "lon": 2.3522, "horizon_h": 24 },
{ "lat": 40.7128, "lon": -74.006, "date": "2026-07-06" },
{ "lat": 35.68, "lon": 139.69, "valid_time": "2026-07-05T12:00:00Z", "variables": ["temperature", "wind"] }
]
}
| Field | Type | Required | Description |
|---|---|---|---|
queries | object[] | yes | Forecast items to resolve, from 1 item up to the batch cap advertised in the live catalog |
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 840 |
valid_time | string | one of target fields | Forecast target instant in RFC 3339 UTC form |
date | string | one of target fields | Forecast day as YYYY-MM-DD, served at 12:00 UTC of that day |
variables | string[] | no | Subset of temperature, precipitation, wind; all by default |
Each item locates the point with explicit lat/lon (place-name city
resolution is a single-endpoint convenience and is not offered in batch) and
carries exactly one target field - horizon_h, valid_time or date, with
the same forms, bounds and semantics as
GET /weather/forecast. Items are
independent: they may mix target forms and variable subsets freely.
200 response - UnifiedResponse
{
"data": {
"count": 3,
"results": [ { ... }, { ... }, { ... } ]
},
"provenance": {
"source": "gfs-noaa",
"fetched_at": "2026-07-04T12:00:00Z",
"freshness": { "kind": "live" }
}
}
count: number of items resolved, equal toresults.lengthand to the number of submitted items.results: one verdict per query, in the same order as the input. Each element has the same shape as thedataobject returned byGET /weather/forecast, includinggrid,leadandcoverage.- A single
provenanceblock covers the whole batch, set from the first covered item (its served cycle and source).
Example - mixed coverage batch
A covered item and an out-of-window item can coexist in the same paid answer. The
uncovered item stays a verdict in results with null values,
coverage.complete: false and no lead (nothing was served); it does not
make the whole batch fail.
{
"data": {
"count": 2,
"results": [
{
"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-07-04T00:00:00Z",
"valid_time": "2026-07-05T00:00:00Z",
"horizon_h": 24,
"step_time": "2026-07-05T00:00:00Z"
},
"coverage": { "complete": true }
},
{
"lat": 48.8566,
"lon": 2.3522,
"horizon_h": 840,
"temperature": { "celsius": null },
"precipitation": { "total_mm": null },
"wind": { "speed_ms": null, "direction_deg": null },
"coverage": {
"complete": false,
"reason": "requested lead time is outside the available forecast window 2026-07-04T00:00:00+00:00..2026-08-07T06:00:00+00:00"
}
}
]
},
"provenance": {
"source": "gfs-noaa",
"fetched_at": "2026-07-04T12:00:00Z",
"freshness": { "kind": "live" }
}
}
Coverage honesty
Both registers are gridded forecast models, not weather station readings, and the two-register composite window (deterministic GFS then the GEFS ensemble mean) applies per item exactly as on the single endpoint - see Two forecast registers. Each result is the nearest or interpolated grid value served for that item, with the same coverage semantics.
A per-item failure is local: an item outside every covered window - or on a
grid cell with a data gap - returns null values with coverage.complete: false
inside a 200 batch. The whole call returns 400 OUT_OF_RANGE only when
no item is covered at all (zero value delivered, so the agent does not pay); if
no forecast cycle has been ingested, it returns 503 NO_FORECAST_AVAILABLE, a
transient no-charge state.
Batch cap and settlement
The gateway prices this endpoint per query and settles the batch as one x402
payment for the call: one settlement, but the amount scales with the number of
items. The current cap and per-query price are published in the live
/catalog, including the unit field used for the batch size;
the request body uses that same field, queries. This page never hardcodes the
price.
An oversized batch is rejected before any lookup work is performed, so it is not a paid answer. Split larger fan-outs into multiple calls using the catalog’s published cap.
Errors
Only requests the service cannot answer leave the 200 range.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_BODY | Body missing, not JSON, not an object, or queries missing |
| 400 | EMPTY_BATCH | queries is an empty array ([]) |
| 400 | BATCH_TOO_LARGE | More items than the published batch cap |
| 400 | INVALID_QUERY | An item is malformed, has invalid coordinates, has no single valid target, or names an unknown variable |
| 400 | OUT_OF_RANGE | No item in the batch is covered by any forecast window |
| 503 | NO_FORECAST_AVAILABLE | No forecast cycle is currently available in the store |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "missing 'queries' array in request body", "code": "INVALID_BODY" }
{ "error": "'queries' must contain at least one item", "code": "EMPTY_BATCH" }
{ "error": "query at index 1: provide exactly one of 'valid_time', 'horizon_h' or 'date'.", "code": "INVALID_QUERY" }
{ "error": "requested lead time is outside the available forecast window 2026-07-04T00:00:00+00:00..2026-08-07T06:00:00+00:00", "code": "OUT_OF_RANGE" }
See also
GET /weather/forecast- single point and target reference with full field documentation.GET /weather/forecast/daily- a whole day-by-day series for one point in one settlement.- For agents - discovery surfaces, the live
/catalogand how settlement works.