GET /climate/point

Returns historical climate variables for one point and one date - the point given as GPS coordinates or as a city name (city=Monaco): 2 m temperature, total precipitation and 10 m wind, served from ERA5 reanalysis data decoded from GRIB into a local grid store. The runtime lookup is local, so a covered point resolves in milliseconds without an account or API key.

This is useful when an agent needs climate evidence it cannot regenerate from text alone: insurance parametric checks, agriculture analysis, energy planning or logistics decisions tied to a dated location. 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 date, an uncovered date or an unknown variable - leave the 200 range.

Parameters

ParameterTypeRequiredDescription
latnumbercoordinate modeLatitude in decimal degrees, from -90 to 90
lonnumbercoordinate modeLongitude in decimal degrees, from -180 to 180
citystringplace-name modePlace name instead of coordinates, for example city=Monaco
locationstringnoStrict alias of city; if both are present they must be identical
countrystringnoISO-3166 alpha-2 code narrowing an ambiguous place name, for example country=US
datestringyesUTC date to query, format YYYY-MM-DD
variablesstringnoComma-separated subset of temperature, precipitation, wind; all by default

Locate the point either with lat/lon coordinates or with a place name via city (alias location) - never both. Place names resolve against an embedded GeoNames cities500 gazetteer: the match is exact but case- and accent-insensitive (no fuzzy matching), an ambiguous name resolves to the most populous candidate (city=Paris is Paris, France; add country=US for the Texas homonym), and an unresolvable name is a 400 UNKNOWN_LOCATION - no charge. The resolved place is echoed back in data.location, so the agent sees exactly which city answered.

GET /climate/point?lat=48.8566&lon=2.3522&date=2024-07-14
GET /climate/point?city=Monaco&date=2024-07-14

Use variables to reduce the payload when the agent only needs one family:

GET /climate/point?lat=48.8566&lon=2.3522&date=2024-07-14&variables=temperature,wind

200 response - UnifiedResponse

{
  "data": { ... },
  "provenance": {
    "source": "era5-copernicus",
    "fetched_at": "2026-06-20T12:00:00Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-06-19T00:00:00Z" }
  }
}
  • provenance.source: stable identifier of the served store source, typically era5-copernicus.
  • freshness.kind: snapshot for ERA5 reanalysis; as_of is the store production date that backed the answer.
  • ERA5 values are derived from Copernicus Climate Change Service information.

Fields of data

FieldTypeDescription
latnumberLatitude in effect for the answer: echoed, or resolved from the place name
lonnumberLongitude in effect for the answer: echoed, or resolved from the place name
locationobjectResolved place echo; present only when the request used city/location
datestringDate exactly echoed in YYYY-MM-DD form
temperatureobjectRequested 2 m temperature; omitted when not requested
precipitationobjectRequested total precipitation; omitted when not requested
windobjectRequested 10 m wind; omitted when not requested
gridobjectEffective grid cell used for the answer; omitted if no cell served
coverageobjectCompleteness marker for the requested variables

location

Present only in place-name mode - when the request located the point with city/location. Calls made with explicit lat/lon never carry this block, and their responses are unchanged.

FieldTypeDescription
namestringCanonical gazetteer name of the resolved place, e.g. Zürich
countrystringISO-3166 alpha-2 country code of the resolved place
admin1stringFirst-level administrative division; can be empty
latnumberLatitude the point was resolved to
lonnumberLongitude the point was resolved to
sourcestringAlways GeoNames

temperature

FieldTypeDescription
celsiusnumber | null2 m temperature in Celsius

precipitation

FieldTypeDescription
total_mmnumber | nullDaily total precipitation in millimetres

wind

FieldTypeDescription
speed_msnumber | null10 m wind speed in metres per second
direction_degnumber | nullMeteorological direction in degrees: 0 = north, 90 = east

Wind is the daily-mean wind vector: speed_ms is the magnitude of the mean vector (lower than the mean wind speed when direction varies during the day), and direction_deg is the resultant direction.

grid

FieldTypeDescription
latnumberLatitude of the representative grid cell
lonnumberLongitude of the representative grid cell
distance_kmnumberDistance from the requested point to that cell

coverage

coverage tells the agent whether every requested variable was actually available at the served grid cell.

FieldTypeDescription
completebooltrue when all requested variables have values
reasonstringPresent when complete: false, naming the missing family data

Example - covered point

{
  "data": {
    "lat": 48.8566,
    "lon": 2.3522,
    "date": "2024-07-14",
    "temperature": { "celsius": 22.4 },
    "precipitation": { "total_mm": 1.2 },
    "wind": { "speed_ms": 4.8, "direction_deg": 216.9 },
    "grid": { "lat": 48.75, "lon": 2.25, "distance_km": 13.42 },
    "coverage": { "complete": true }
  },
  "provenance": {
    "source": "era5-copernicus",
    "fetched_at": "2026-06-20T12:00:00Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-06-19T00:00:00Z" }
  }
}

Example - place-name request

A request made with city= answers with the same fields, plus the location echo naming the resolved place. data.lat/data.lon carry the resolved coordinates.

GET /climate/point?city=Zurich&date=2024-07-14&variables=temperature
{
  "data": {
    "lat": 47.36667,
    "lon": 8.55,
    "location": {
      "name": "Zürich",
      "country": "CH",
      "admin1": "Zurich",
      "lat": 47.36667,
      "lon": 8.55,
      "source": "GeoNames"
    },
    "date": "2024-07-14",
    "temperature": { "celsius": 19.7 },
    "grid": { "lat": 47.25, "lon": 8.5, "distance_km": 13.55 },
    "coverage": { "complete": true }
  },
  "provenance": {
    "source": "era5-copernicus",
    "fetched_at": "2026-06-20T12:00:00Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-06-19T00:00:00Z" }
  }
}

Example - covered date, missing variable value

A requested date can be covered while a grid cell still has a missing value for one family. That remains a successful answer: the value is null and coverage.complete explains the gap.

{
  "data": {
    "lat": 48.8566,
    "lon": 2.3522,
    "date": "2024-07-14",
    "temperature": { "celsius": null },
    "precipitation": { "total_mm": 1.2 },
    "wind": { "speed_ms": 4.8, "direction_deg": 216.9 },
    "grid": { "lat": 48.75, "lon": 2.25, "distance_km": 13.42 },
    "coverage": {
      "complete": false,
      "reason": "no data at this grid cell for: temperature"
    }
  },
  "provenance": {
    "source": "era5-copernicus",
    "fetched_at": "2026-06-20T12:00:00Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-06-19T00:00:00Z" }
  }
}

Coverage honesty

ERA5 is a gridded reanalysis, 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. Daily values are aggregate values from the store, not sub-hourly observations.

The historical window is finite, moves with the ingested store, and differs by variable: 2 m temperature and total precipitation cover a rolling 30-year window (the WMO-standard climatological period), while 10 m wind covers a rolling ~5-year window. A date covered for some requested variables but not others still returns 200: the uncovered family is null and coverage.complete: false explains the gap - the same shape as a local grid gap. A date outside the window of every requested variable returns 400 OUT_OF_RANGE, because the service cannot answer the request.

Errors

Only requests the service cannot answer leave the 200 range.

StatuscodeCase
400INVALID_COORDSlat/lon non-numeric or outside valid bounds, or a point-mode conflict (coordinates and a place name together, city differing from location, or no point at all)
400UNKNOWN_LOCATIONcity/location names a place the gazetteer cannot resolve (or place-name resolution is unavailable on the deployment)
400INVALID_DATEdate missing or not formatted as YYYY-MM-DD
400OUT_OF_RANGERequested date is outside the covered store window
400INVALID_VARIABLEvariables contains a family outside the supported set
500INTERNALInternal error (detail logged, not exposed)

Error messages are written to guide the retry: an unknown place suggests the spelling check, the country= filter and the lat/lon fallback - and a 4xx is never charged, so the corrected call is the only one the agent pays for.

{ "error": "unknown place 'Xyzzy'; check the spelling, narrow with 'country' (ISO-3166 alpha-2), or provide lat/lon instead", "code": "UNKNOWN_LOCATION" }
{ "error": "invalid date '14-07-2024', expected YYYY-MM-DD", "code": "INVALID_DATE" }
{ "error": "requested date is outside the covered window 1996-06-15..2026-06-14", "code": "OUT_OF_RANGE" }

See also

  • GET /weather/forecast - point weather forecast from GFS, for future dates and lead times.
  • For agents - discovery surfaces, the live /catalog and how settlement works.