How far from normal was this month?
ESG reports and parametric triggers need a departure from a stable baseline, not a raw reading. One x402 call returns the ERA5 value, the 1991-2020 WMO normal, and the anomaly between them.
“This July was 2.7 °C above normal here” is a sentence an ESG report or a
parametric trigger lives on — and it is only meaningful if normal is a stable,
named baseline rather than a convenient short window. An agent producing that
statement needs the observed value, the climatological normal, and the difference
between them, computed against a reference it can cite. One paid call —
GET /climate/anomaly — returns exactly that: the
departure of the observed ERA5 value from the 1991-2020 WMO climate normal,
for temperature, precipitation and wind.
The problem: an anomaly is only as good as its baseline
The failure mode here is subtle. You can compute “above average” against anything — last year, the last five years, a fitted trend — and get a number that looks authoritative and means nothing comparable. A defensible anomaly is measured against a standard baseline: the WMO 30-year climate normal, currently the 1991-2020 period (WMO-No. 1203). Mix baselines across a report and the numbers stop being comparable.
GET /climate/anomaly uses the 1991-2020 normal as its only reference and
stamps it into every response, so the baseline is never ambiguous.
The call: a point and a window
Give a latitude, a longitude, an inclusive date range, and optionally the families you want:
GET /climate/anomaly?lat=48.8566&lon=2.3522&from=2024-07-01&to=2024-07-31
GET /climate/anomaly?lat=48.8566&lon=2.3522&from=2024-07-01&to=2024-07-31&variables=temperature
The window [from, to] is the unit of the product, capped at 366 days per
call. For each requested family the response gives the three numbers an anomaly
needs — observed, normal, and their difference:
| Family | Observed | Normal (1991-2020) | Anomaly |
|---|---|---|---|
temperature | observed_celsius | normal_celsius | anomaly_celsius |
precipitation | observed_mm | normal_mm | anomaly_mm |
wind | observed_speed_ms | normal_speed_ms | anomaly_speed_ms |
The anomaly is always observed minus normal, and the baseline used is echoed
in reference_period (e.g. 1991-2020).
Honesty: it is observed minus a 30-year monthly normal
This is the scope that makes the figure citable:
- The baseline is fixed and stated. The reference is the 1991-2020 WMO normal
— a 30-year baseline — surfaced in
reference_period. The endpoint never dresses up a short window as a “normal.” - The normal is monthly. Each observed day is paired with the normal for its month, so over a multi-month window the normals are weighted by how many days fall in each month. Temperature and wind are means; precipitation is the observed total versus the sum of the daily normals.
- It is a grid value, not a station. ERA5 is a gridded reanalysis on a mesh
of about 0.25 degrees;
grid.distance_kmexposes how far the served cell sits from the requested point.
An unpaired family is null, with the reason
An anomaly needs both an observation and a normal. A day counts only when it
has both; a family that cannot be paired comes back null, and coverage
distinguishes the two causes — an observation gap versus a
climatological normal not available. The request still succeeds at 200 as
long as another family was paired.
{
"data": {
"lat": 48.8566, "lon": 2.3522,
"from": "2024-07-01", "to": "2024-07-31",
"reference_period": "1991-2020",
"days_counted": 31,
"temperature": { "observed_celsius": 22.8, "normal_celsius": 20.1, "anomaly_celsius": 2.7 },
"wind": null,
"grid": { "lat": 48.75, "lon": 2.25, "distance_km": 13.42 },
"coverage": { "complete": false, "reason": "wind: climatological normal not available" }
},
"provenance": {
"source": "era5-copernicus",
"freshness": { "kind": "snapshot", "as_of": "2026-06-19T00:00:00Z" }
}
}
That distinction matters for a reporting agent: “no observation” and “no normal”
are different gaps, and the response tells you which one you hit. Per the x402
golden rule, only requests the service genuinely cannot answer leave the 200
range: invalid coordinates (INVALID_COORDS), a bad or over-long period
(INVALID_PERIOD), an unknown family (INVALID_VARIABLE), or a window where
nothing can be paired (OUT_OF_RANGE).
Where it fits in the x402 loop
Each paid call follows the same pattern as every Invoket endpoint. The
Quickstart walks the discover → 402 → pay → replay cycle
with runnable snippets, and For agents covers the discovery
surfaces and the live /catalog. Price and accepted rails are not pinned in this
article — they are served live by the
catalog; see the
endpoint reference for the current figure.
Raw value, balance, departure, rarity
Anomaly is the departure layer of the climate family — pick the endpoint that matches the question:
GET /climate/point— the raw daily values for a single date (see Historical weather for any GPS point and date).GET /climate/aggregate— the period balance, means and totals, when you want the level rather than the departure.GET /climate/indices— threshold event counts for parametric triggers.GET /climate/return-period— once you know this month was unusual, how rare was it, empirically.
Reach for anomaly when the deliverable is “above/below normal by how much” — parametric insurance and ESG / climate reporting.
Used for what it is — an observed-minus-normal departure against the fixed
1991-2020 WMO baseline, with the reference period stamped in and the served cell
and coverage exposed honestly — GET /climate/anomaly gives an agent a citable
deviation, without an account and without building a climatology from GRIB. For
the full field reference, baseline semantics and error codes, see the
GET /climate/anomaly documentation.
ERA5 values are derived from Copernicus Climate Change Service (C3S) information.