# How far from normal was this month? The ERA5 climate anomaly > ESG reports and parametric triggers need a departure from a stable baseline, not a raw reading. One paid call returns the ERA5 value, the 1991-2020 WMO normal, and the anomaly between them. Published 2026-06-28 · Updated 2026-08-16 · HTML version: https://invoket.com/blog/how-far-from-normal-was-this-month --- "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`](/docs/api/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: ```http 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_km` exposes 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. ```json { "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](/docs/quickstart) walks the discover → `402` → pay → replay cycle with runnable snippets, and [For agents](/docs/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](https://api.invoket.com/catalog); see the [endpoint reference](/docs/api/climate-anomaly) 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`](/docs/api/climate-point) — the raw daily values for a single date (see [Historical weather for any GPS point and date, from ERA5](/blog/historical-weather-for-any-gps-point)). - [`GET /climate/aggregate`](/docs/api/climate-aggregate) — the period balance, means and totals, when you want the level rather than the departure. - [`GET /climate/indices`](/docs/api/climate-indices) — threshold event counts for parametric triggers. - [`GET /climate/return-period`](/docs/api/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](/docs/api/climate-anomaly). ERA5 values are derived from Copernicus Climate Change Service (C3S) information.