climatememory developers

API reference

Weather API

Global forecasts to 15 days from ECMWF and the national high-resolution models, terrain-corrected to your real ground elevation. One scope — meteo — covers this whole page.

Forecast#

GEThttps://api.climatememory.com/v1/forecastscope: meteo1 credit

Hourly and daily forecast for an arbitrary coordinate.

Parameters

ParameterTypeDefaultDescription
latfloatLatitude, −90 to 90. Required.
lonfloatLongitude, −180 to 180. Required.
daysint71–16 is accepted, but the served horizon is 15 days, so a 16 returns 15. Days 11–15 are a trend, not a forecast — see below.
elevationfloat (m)True ground elevation of your point. Supply it and the temperature is corrected for the difference against the model's smoothed terrain. Typically worth 1–3 °C in hills.
timezoneIANA zoneUTCThe zone daily aggregates are cut on, and the offset every timestamp carries back.
hourlycsvallSubset of hourly fields, to shrink the payload. Names are those in the field reference.
include_dailybooltrueInclude the daily block.

Response

{
  "latitude": 48.85,
  "longitude": 2.35,
  "timezone": "Europe/Paris",
  "grid": {
    "latitude": 48.85, "longitude": 2.35,
    "distance_km": 0.42, "resolution_km": 1.3
  },
  "elevation": {
    "applied": true,
    "model_elevation_m": 91.0,
    "location_elevation_m": 113,
    "temperature_offset_c": -0.14
  },
  "source": {
    "model": "mf_arome",
    "model_run": "20260801T00Z",
    "reference_time": "2026-08-01T00:00:00+00:00",
    "data_age_hours": 3.95,
    "stale": false,
    "attribution": "Data: Météo-France (etalab-2.0)"
  },
  "hourly": {
    "time": ["2026-08-01T00:00:00+02:00", "..."],
    "temperature_2m": [18.1, 17.9, 17.6, "..."],
    "apparent_temperature": [17.4, "..."],
    "weather_code": [0, 1, 2, "..."]
  },
  "daily": {
    "date": ["2026-08-01", "..."],
    "temperature_2m_max": [26.6, "..."],
    "temperature_2m_min": [16.2, "..."],
    "precipitation_sum": [0.0, "..."],
    "uv_index_max": [7.6, "..."],
    "sunrise": ["2026-08-01T06:24:00+02:00", "..."],
    "sunset": ["2026-08-01T21:24:00+02:00", "..."],
    "daylight_hours": [15.0, "..."]
  }
}

The grid block is worth reading. distance_km tells you how far the model cell centre is from the point you asked for, and resolution_km how coarse that cell is. Together they tell you how literally to take the numbers: 0.4 km from a 1.3 km cell is your street; 12 km from a 28 km cell is your region.

The elevation block says what was done, not what was asked. applied: false means no correction happened — either you passed no elevation, or the model's terrain already matched. Never assume the correction ran because you requested it.

Shrinking the payload

The full hourly block for 15 days is around 32 fields × 360 hours. If you plot three of them, ask for three:

GET /v1/forecast?lat=48.85&lon=2.35&days=3
    &hourly=temperature_2m,precipitation,weather_code
    &include_daily=false

Same price — the cost is reading the archive, not serialising it — but a tenth of the bytes and noticeably faster to parse on a phone.

Forecast by city#

GEThttps://api.climatememory.com/v1/forecast/city/{country}/{slug}scope: meteo1 credit

Same response, resolved through the city catalogue.

Prefer this over raw coordinates where you can. The catalogue supplies two things a coordinate cannot: the city's true elevation, so the terrain correction applies without you supplying it, and its time zone, so daily aggregates are cut on the right local day.

GET /v1/forecast/city/dz/alger      # French slug
GET /v1/forecast/city/dz/algiers    # English slug — same city
GET /v1/forecast/city/fr/paris

country is an ISO-3166-1 alpha-2 code, lower case. slug is accent-insensitive: bejaia finds Béjaïa. An unknown slug returns 404 city_not_found with a suggestions array — show it rather than a dead end.

All the query parameters of /v1/forecast still apply except lat, lon and elevation, which the catalogue provides. Passing timezone overrides the city's own, which is almost never what you want.

Do not hard-code slugs you guessed. Resolve the name once through /v1/geocode, store the country and slug it returns, and use those. Geocoding costs no credits, so this is free to do properly.

Current conditions#

GEThttps://api.climatememory.com/v1/currentscope: meteo1 credit

Conditions right now for a coordinate, interpolated between model steps.

GEThttps://api.climatememory.com/v1/current/city/{country}/{slug}scope: meteo1 credit

The same, through the city catalogue.

Temperature, humidity, wind and pressure are interpolated linearly to the current instant, between the two model steps that bracket it.

Precipitation is not interpolated, and that is deliberate. An hourly precipitation value is a total over an interval, not a reading at an instant. Interpolating it would invent rain in a minute the model placed in a different hour. You get the containing step's value instead — a real number about a real interval.

Use this for a "right now" display. For anything you will compare across time, use /v1/forecast and read the hour you want: the series is stable, whereas now moves under you between two calls.

Field reference#

GEThttps://api.climatememory.com/v1/fieldsno key needed0 credits

The same table as JSON, with units and which fields are default. No key needed.

Hourly fields

21 of these come back by default. The rest are yours for the asking — name them in hourly=, comma-separated — and they are held back for cost, not for doubt: each stored field is a separate compressed read, so a default response carrying all 45 would make every caller pay for the few who want snow depth.

GET /v1/forecast?lat=45.19&lon=5.72&hourly=temperature_2m,snow_line_altitude,soil_temperature_8cm

A field a model does not publish comes back as null for that model's stretch of the forecast rather than vanishing from the response — so a chain that starts on AROME and continues on ICON-EU returns visibility throughout, null for the first 51 hours. An unknown field name is a 400 unknown_field, with the closest match suggested; it used to be silently ignored.

FieldUnitNotes
temperature_2m°CTerrain-corrected when elevation is known
apparent_temperature°CSteadman formulation; valid across the whole range
relative_humidity_2m%
dewpoint_2m°C
precipitationmmTotal over the step, de-cumulated
precipitation_probability_proxy%Heuristic, not an ensemble probability. See below.
weather_codeWMOIcon code, thresholded on mm/h not on the step total
cloud_cover%
wind_speed_10mm/s
wind_direction_10m°Direction the wind blows from
wind_gust_10mm/sMaximum over the step
pressure_mslhPaReduced to sea level
surface_pressurehPaAt the location's elevation
shortwave_radiationW/m²Mean over the step
uv_index0–11+Estimated. See below.
capeJ/kgThunderstorm potential
is_day0/1
visibilitymICON-EU and ICON-D2 only
cloud_cover_low%ICON models only
cloud_cover_mid%ICON models only
cloud_cover_high%ICON models only
precipitation_typeWMOECMWF only Ask for it in hourly=
snow_depthmWhat is lying, not what falls. ICON only Ask for it in hourly=
snow_water_equivalentmmWhat the snowpack yields on melting. ICON only Ask for it in hourly=
snow_line_altitudemSee snow. ICON-EU and ICON-D2 Ask for it in hourly=
freezing_level_altitudem0 °C isotherm. ICON only Ask for it in hourly=
soil_temperature_0cm°CSurface. ICON global and ICON-EU only Ask for it in hourly=
soil_temperature_8cm°CRoot zone, 7–28 cm layer. ICON global and ICON-EU only Ask for it in hourly=
shortwave_radiation_directW/m²Beam component. ICON only Ask for it in hourly=
shortwave_radiation_diffuseW/m²Sky component. ICON only Ask for it in hourly=
solar_elevation°Computed from the timestamp and the coordinate Ask for it in hourly=
solar_azimuth°Computed from the timestamp and the coordinate Ask for it in hourly=
temperature_2m_max°COver the step, where the model publishes it Ask for it in hourly=
temperature_2m_min°COver the step, where the model publishes it Ask for it in hourly=
skin_temperature°CGround surface, not air Ask for it in hourly=
heat_index°CUS NWS. Returns plain temperature below 27 °C Ask for it in hourly=
wind_chill°CEnvironment Canada. Returns plain temperature above 10 °C Ask for it in hourly=
wind_speed_100mm/sWind-energy applications Ask for it in hourly=
wind_direction_100m°Ask for it in hourly=
wind_beaufort0–12Ask for it in hourly=
cloud_cover_octas0–8Ask for it in hourly=
snowfallmmWater equivalent, not depth of fresh snow Ask for it in hourly=
total_column_water_vapourkg/m²Ask for it in hourly=
pressure_tendencyhPaAsk for it in hourly=
weather_descriptiontextAsk for it in hourly=

Daily fields

date, temperature_2m_max, temperature_2m_min, temperature_2m_mean, precipitation_sum, wind_speed_max, wind_gust_max, shortwave_radiation_sum (MJ/m²), uv_index_max, sunrise, sunset, daylight_hours, weather_code.

Aggregated on local calendar days, not UTC. A partial day at either end of the range is omitted rather than reported with a misleading maximum — so a 7-day request can legitimately return 6 daily rows.

Two fields are estimates, and we would rather say so than have you find out.

uv_index is derived from solar elevation and broadband radiation, not from an ozone column. Accurate to roughly ±1 unit — enough for "wear a hat", not for a medical claim.

precipitation_probability_proxy is an estimate, not a probability. A deterministic model has no spread to derive one from. It is named _proxy so nobody mistakes it for the ensemble product served by /v1/probability, which is a genuine frequency over 51 members.

It combines two terms. The first asks whether the cell's rain reaches your point: the stored rate is an average over the whole grid cell, so at 28 km — 780 km² — a light mean may be a real shower over a small part of it. Taking the sub-grid rate distribution as Weibull, with a shape set by the cell size and the model's own mean, gives the chance a point in the cell exceeds 0.1 mm/h in closed form. The second term bounds what an overcast, near-saturated sky may claim where the model puts no rain in the cell at all. Both are then scaled by how much a run deserves to be believed at that lead time, which is why the same rainfall reads lower on day 9 than on day 1.

Consequently the same place can read differently from two models, and should: a 2.2 km cell and a 28 km cell genuinely disagree about what a light average means. Consumer apps that quote one number hide this.

Models and resolution#

GEThttps://api.climatememory.com/v1/modelsno key needed0 credits

Which models are live, what each covers, how fresh each is. No key needed.

We serve the finest model that covers your point and your lead time. You never choose one; the choice is reported in source.model so you can always tell which answered.

High-resolution models are all short-range — at 1 km the atmosphere becomes chaotic within two days, so forecasting further would be meaningless — and the resolution therefore steps down as the forecast advances:

day 0 ─────── day 2 ─────── day 5 ───────────────── day 15
  AROME 1.3 km (France)
  ICON-D2 2.2 km (Germany, Alps, Benelux)
  HRRR 3 km (USA and southern Canada)
              ICON-EU 6.5 km (Europe)
                          ICON 13 km · ECMWF 28 km (global)
                                              AIFS (trend only)
source.modelModelResolutionRunsHorizon
mf_aromeMétéo-France AROME1.3 km8/day51 h
dwd_icon_d2DWD ICON-D22.2 km4/day48 h
noaa_hrrrNOAA HRRR3 km4/day48 h
dwd_icon_euDWD ICON-EU6.5 km2/day120 h
dwd_iconDWD ICON13 km2/day180 h
ecmwf_ifsECMWF IFS28 km2/day240 h
ecmwf_aifsECMWF AIFS28 km2/day360 h
ecmwf_waveECMWF wave28 km2/day240 h

Transitions across a model's domain edge are cross-faded, so two towns either side of a boundary never disagree by a step change.

Days 11–15 are a trend, not a forecast. At that range skill approaches climatology. We publish it because it is asked for; treat it as direction of travel, and if you display it, say so.

/v1/models needs no key, which makes it the right thing to poll from a status page or to check before you buy: it reports each model's latest run and its age, so "is the data fresh" is answerable without spending a credit.

Ensemble probabilities#

GEThttps://api.climatememory.com/v1/probabilityscope: meteo1 credit

Percentiles and rain probabilities from the 51-member ECMWF ensemble.

A single forecast says 22 °C on Thursday. That is a guess presented as a fact. This endpoint answers the question you actually decide on: how sure is it, and what is the chance of rain worth planning around.

It is computed from ECMWF's ensemble — the same model run 50 times from slightly different starting conditions. Where the runs agree, the forecast is confident. Where they scatter, the atmosphere itself is uncertain, and no model however good can tell you more.

{
  "members": 50,
  "hourly": {
    "time": ["2026-08-01T00:00:00+00:00"],
    "temperature_2m_p10": [26.4],
    "temperature_2m_p50": [26.78],
    "temperature_2m_p90": [27.3],
    "temperature_2m_spread": [0.4],
    "precipitation_probability_0_1mm": [10.0],
    "precipitation_probability_1_0mm": [0.0],
    "precipitation_probability_5_0mm": [0.0],
    "precipitation_probability_10_0mm": [0.0],
    "precipitation_p90": [0.1]
  }
}

How to read it

p10 and p90 bracket the middle 80 % of the members: one in ten expects it colder than p10, one in ten warmer than p90. A spread of 0.4 °C is a settled situation you can trust; 3 °C means the models disagree and you should say so to your users rather than pick one.

The rain thresholds are decisions rather than round numbers — 0.1 mm is wet at all, 1 mm is take a coat, 5 and 10 mm are this is a problem. precipitation_p90 is the bad case: only one member in ten is wetter.

Two variables only, temperature and precipitation. Adding cloud, wind and pressure would double the bandwidth of the whole platform for numbers nobody makes a decision on.

Air quality#

GEThttps://api.climatememory.com/v1/air-qualityscope: meteo1 credit

Particulates, ozone, NO₂, SO₂, CO and Saharan dust, from Copernicus CAMS.

This is the endpoint that matters most across North Africa and the Mediterranean. A dust episode takes PM10 past a thousand micrograms per cubic metre for days at a time, which is a health decision rather than a number, and it is poorly covered by free consumer services.

{
  "current": {
    "pm2_5": 38.7,
    "band": "poor",
    "who_guidelines_ug_m3": {"pm2_5": 15.0, "pm10": 45.0,
                             "nitrogen_dioxide": 25.0, "ozone": 100.0}
  },
  "hourly": {
    "time": ["2026-08-01T00:00:00+00:00"],
    "pm2_5": [38.7], "pm10": [85.8], "ozone": [43.4],
    "nitrogen_dioxide": [19.0], "dust_aod_550nm": [0.24]
  },
  "units": {"pm2_5": "ug/m3", "pm10": "ug/m3", "dust_aod_550nm": "1"}
}

All concentrations are in µg/m³, the unit air quality is quoted in everywhere. dust_aod_550nm is an optical depth and has no unit: above about 0.5 the sky is visibly hazy, above 1.0 the sun is dimmed.

The WHO short-term guideline values ride along in every response so a number can be placed without looking it up. band follows the European air quality index for PM2.5: good, fair, moderate, poor, very poor, extremely poor.

Snow and the snow line#

Four fields, and they answer different questions. snowfall is how much falls; the rest describe what is on the ground and where.

Ask for them by name — they are not in the default response:

GET /v1/forecast?lat=45.19&lon=5.72&hourly=snowfall,snow_depth,snow_water_equivalent,snow_line_altitude,freezing_level_altitude
FieldUnitWhat it tells you
snow_depthmWhat is lying. Twenty centimetres can fall and melt, or land on eighty already there.
snow_water_equivalentmmWhat it yields on melting. Half a metre of powder and half a metre of packed snow are very different.
snow_line_altitudemThe altitude above which precipitation falls as snow.
freezing_level_altitudemHeight of the 0 °C isotherm, typically a few hundred metres above the snow line.

The snow line is the one worth reading. "It rains at 800 m and snows at 1200" is a decision a ski resort, a road authority or a driver can act on; "3 mm of precipitation" is not. It matters in North Africa too — the Atlas has ski areas at Chréa and Tikjda, and the high plateaux at Sétif, Batna and Djelfa sit above 1000 m.

Glaciers report tens of metres of snow depth, because that is how the model represents permanent ice rather than a measurement error. The deepest seasonal snow on earth is around 11 m — treat anything past that as ice, not weather.

These come from DWD's ICON, which covers the world at 13 km. ECMWF's open data publishes snow depth but not the snow line, so this is one place where the coarser model is the more useful one. snow_line_altitude is a regional product: ICON-EU and ICON-D2 publish it, ICON global does not, so outside Europe you get the freezing level and the depth but not the line itself.

Marine — sea state#

GEThttps://api.climatememory.com/v1/marinescope: meteo1 credit

Wave height, direction and period from the ECMWF wave model, worldwide, to 10 days.

{
  "hourly": {
    "time": ["2026-08-01T00:00:00+00:00"],
    "wave_height": [0.32],
    "wave_direction": [264.3],
    "wave_period": [3.79],
    "wave_peak_period": [4.21]
  }
}

Wave height is the significant height — the mean of the highest third of the waves, which is roughly what an observer at sea reports. Individual waves reach about twice it, which is the number that matters if you are deciding whether to go out. Direction is where the waves come from, like wind direction.

A point on land returns 404 not_at_sea rather than a list of nulls. The wave model has no value over land by construction, and saying so is more useful than a response that reads like a service failure. If you let users drop a pin, handle this code explicitly.

Rivers#

GEThttps://api.climatememory.com/v1/hydrologyscope: meteo1 credit

River discharge from Copernicus GloFAS on a 0.05° river network, to 10 days.

It answers what no weather model can: not how much rain falls upstream, but how much water arrives here — a different number, separated from the rain by a catchment, a soil moisture state and a day or two of travel time.

{
  "disclaimer": "Information only. Only national and regional authorities
                 are authorised to issue flood warnings.",
  "daily": {
    "time": ["2026-08-01T00:00:00+00:00"],
    "river_discharge": [4.953],
    "soil_wetness_index": [0.448]
  }
}
FieldMeaning
river_dischargeMean flow in m³/s over the 24 hours ending at that time. Zero means the model has no river in that cell, not that a river ran dry.
soil_wetness_indexCatchment saturation, 0 dry to 1 saturated. High values mean further rain runs off rather than soaking in.

This is not a flood warning and must not be presented as one. The Copernicus licence reserves flood warnings to national and regional authorities within their area of responsibility, and that is the right arrangement — the people authorised to warn are the ones who can also close a road and evacuate a village.

Discharge in m³/s, yes. An alert, a severity colour or an instruction to act, no, whatever the numbers look like on the day. The disclaimer travels in every response body, not only here, and it is there so that it reaches your users even if this page did not.