> ## Documentation Index
> Fetch the complete documentation index at: https://docs.climatifai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Climate data: historical series and projections

> Understand how AgroRisk retrieves, caches, and serves monthly climate data from Open-Meteo ERA5 archives and CMIP6 scenario projections.

The `/agri/climate` endpoint gives you two time series for any coordinate in LATAM: a historical monthly record going back as far as 1991, and a forward-looking projection generated from CMIP6 climate models. Both series share the same data shape, so you can hand them directly to a charting library or pass them into your own analysis pipeline.

## Historical data

Historical records are sourced from the Open-Meteo ERA5 reanalysis archive. Each data point represents a single calendar month at the requested location and contains three variables:

| Field           | Type          | Description                           |
| --------------- | ------------- | ------------------------------------- |
| `year`          | integer       | Calendar year                         |
| `month`         | integer       | Calendar month (1–12)                 |
| `temp_c`        | float \| null | Mean 2 m air temperature (°C)         |
| `precip_mm`     | float \| null | Total precipitation (mm)              |
| `soil_moisture` | float \| null | Volumetric soil water content (m³/m³) |

You control the date range with the `from` and `to` query parameters (both are calendar years). The default window is 1991–2020, matching the standard WMO climate normal period.

## Projection data

Projected data is sourced from Open-Meteo's CMIP6 ensemble. The same three fields (`temp_c`, `precip_mm`, `soil_moisture`) are returned for each projected month. Projections typically extend from the current year to 2050 or beyond depending on the scenario.

### Available scenarios

Pass any of the following values in the `scenario` parameter:

| Scenario   | Emissions pathway                                       |
| ---------- | ------------------------------------------------------- |
| `SSP1-2.6` | Low emissions — aggressive mitigation                   |
| `SSP2-4.5` | Intermediate emissions — current policies trajectory    |
| `SSP3-7.0` | High emissions — regional rivalry (default)             |
| `SSP5-8.5` | Very high emissions — fossil-fuel-intensive development |

<Note>
  The default scenario is `SSP3-7.0`. If you are building an advisory tool, consider offering at least `SSP1-2.6` alongside `SSP3-7.0` so users can see the range of possible futures for their location.
</Note>

## Read-through caching

When a Postgres database is configured, the API uses a read-through cache to avoid redundant upstream calls to Open-Meteo. The first time you request a coordinate, the API fetches from Open-Meteo and stores the result. Subsequent requests for the same coordinate are served from the cache until the data is considered stale.

The `_cache` field in every response tells you what happened:

```json theme={null}
"_cache": {
  "historical": { "hit": true, "stale": false },
  "projected":  { "hit": false, "stale": false },
  "any_stale":  false
}
```

| Field         | Meaning                                                                  |
| ------------- | ------------------------------------------------------------------------ |
| `hit: true`   | Data was served from the local cache                                     |
| `hit: false`  | Data was fetched from Open-Meteo and written to cache                    |
| `stale: true` | Cached data exists but is beyond its TTL; upstream was called to refresh |

<Tip>
  If you are calling `/agri/climate` in a batch workflow and want to warm the cache for a set of coordinates, make the requests sequentially and check `hit: true` on subsequent calls to confirm the cache is populated.
</Tip>

## Coordinate rounding

Coordinates are rounded to four decimal places before being used as cache keys and stored in the database. This means that requests for `lat=20.50001` and `lat=20.5000` resolve to the same cached entry. Pass precise but consistent coordinates to maximise cache hit rates and avoid creating duplicate entries.

## Sample response

The following is a representative response from `GET api.climatifai.com/agri/climate?lat=20.5&lon=-101.0&from=2020&to=2022&scenario=SSP3-7.0`:

```json theme={null}
{
  "lat": 20.5,
  "lon": -101.0,
  "scenario": "SSP3-7.0",
  "historical": [
    { "year": 2020, "month": 1,  "temp_c": 16.2, "precip_mm": 12.4, "soil_moisture": 0.18 },
    { "year": 2020, "month": 2,  "temp_c": 17.8, "precip_mm": 8.1,  "soil_moisture": 0.16 },
    { "year": 2020, "month": 6,  "temp_c": 22.5, "precip_mm": 98.3, "soil_moisture": 0.31 },
    { "year": 2022, "month": 12, "temp_c": 15.9, "precip_mm": 18.0, "soil_moisture": 0.19 }
  ],
  "projected": [
    { "year": 2025, "month": 1,  "temp_c": 17.1, "precip_mm": 11.2, "soil_moisture": 0.17 },
    { "year": 2025, "month": 6,  "temp_c": 23.4, "precip_mm": 91.0, "soil_moisture": 0.29 },
    { "year": 2050, "month": 6,  "temp_c": 26.1, "precip_mm": 78.5, "soil_moisture": 0.24 }
  ],
  "_cache": {
    "historical": { "hit": true,  "stale": false },
    "projected":  { "hit": false, "stale": false },
    "any_stale":  false
  },
  "_upstream_hints": {
    "historical": null,
    "projected":  null
  }
}
```

<Note>
  The `_upstream_hints` field carries any error message returned by Open-Meteo during a fetch. A non-null value means the upstream call succeeded partially or returned a warning. A `null` value means the request completed cleanly.
</Note>
