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#
meteo1 creditHourly and daily forecast for an arbitrary coordinate.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
lat | float | — | Latitude, −90 to 90. Required. |
lon | float | — | Longitude, −180 to 180. Required. |
days | int | 7 | 1–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. |
elevation | float (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. |
timezone | IANA zone | UTC | The zone daily aggregates are cut on, and the offset every timestamp carries back. |
hourly | csv | all | Subset of hourly fields, to shrink the payload. Names are those in the field reference. |
include_daily | bool | true | Include 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#
meteo1 creditSame 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#
meteo1 creditConditions right now for a coordinate, interpolated between model steps.
meteo1 creditThe 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#
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.
| Field | Unit | Notes |
|---|---|---|
temperature_2m | °C | Terrain-corrected when elevation is known |
apparent_temperature | °C | Steadman formulation; valid across the whole range |
relative_humidity_2m | % | |
dewpoint_2m | °C | |
precipitation | mm | Total over the step, de-cumulated |
precipitation_probability_proxy | % | Heuristic, not an ensemble probability. See below. |
weather_code | WMO | Icon code, thresholded on mm/h not on the step total |
cloud_cover | % | |
wind_speed_10m | m/s | |
wind_direction_10m | ° | Direction the wind blows from |
wind_gust_10m | m/s | Maximum over the step |
pressure_msl | hPa | Reduced to sea level |
surface_pressure | hPa | At the location's elevation |
shortwave_radiation | W/m² | Mean over the step |
uv_index | 0–11+ | Estimated. See below. |
cape | J/kg | Thunderstorm potential |
is_day | 0/1 | |
visibility | m | ICON-EU and ICON-D2 only |
cloud_cover_low | % | ICON models only |
cloud_cover_mid | % | ICON models only |
cloud_cover_high | % | ICON models only |
precipitation_type | WMO | ECMWF only Ask for it in hourly= |
snow_depth | m | What is lying, not what falls. ICON only Ask for it in hourly= |
snow_water_equivalent | mm | What the snowpack yields on melting. ICON only Ask for it in hourly= |
snow_line_altitude | m | See snow. ICON-EU and ICON-D2 Ask for it in hourly= |
freezing_level_altitude | m | 0 °C isotherm. ICON only Ask for it in hourly= |
soil_temperature_0cm | °C | Surface. ICON global and ICON-EU only Ask for it in hourly= |
soil_temperature_8cm | °C | Root zone, 7–28 cm layer. ICON global and ICON-EU only Ask for it in hourly= |
shortwave_radiation_direct | W/m² | Beam component. ICON only Ask for it in hourly= |
shortwave_radiation_diffuse | W/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 | °C | Over the step, where the model publishes it Ask for it in hourly= |
temperature_2m_min | °C | Over the step, where the model publishes it Ask for it in hourly= |
skin_temperature | °C | Ground surface, not air Ask for it in hourly= |
heat_index | °C | US NWS. Returns plain temperature below 27 °C Ask for it in hourly= |
wind_chill | °C | Environment Canada. Returns plain temperature above 10 °C Ask for it in hourly= |
wind_speed_100m | m/s | Wind-energy applications Ask for it in hourly= |
wind_direction_100m | ° | Ask for it in hourly= |
wind_beaufort | 0–12 | Ask for it in hourly= |
cloud_cover_octas | 0–8 | Ask for it in hourly= |
snowfall | mm | Water equivalent, not depth of fresh snow Ask for it in hourly= |
total_column_water_vapour | kg/m² | Ask for it in hourly= |
pressure_tendency | hPa | Ask for it in hourly= |
weather_description | text | Ask 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#
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.model | Model | Resolution | Runs | Horizon |
|---|---|---|---|---|
mf_arome | Météo-France AROME | 1.3 km | 8/day | 51 h |
dwd_icon_d2 | DWD ICON-D2 | 2.2 km | 4/day | 48 h |
noaa_hrrr | NOAA HRRR | 3 km | 4/day | 48 h |
dwd_icon_eu | DWD ICON-EU | 6.5 km | 2/day | 120 h |
dwd_icon | DWD ICON | 13 km | 2/day | 180 h |
ecmwf_ifs | ECMWF IFS | 28 km | 2/day | 240 h |
ecmwf_aifs | ECMWF AIFS | 28 km | 2/day | 360 h |
ecmwf_wave | ECMWF wave | 28 km | 2/day | 240 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#
meteo1 creditPercentiles 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#
meteo1 creditParticulates, 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
| Field | Unit | What it tells you |
|---|---|---|
snow_depth | m | What is lying. Twenty centimetres can fall and melt, or land on eighty already there. |
snow_water_equivalent | mm | What it yields on melting. Half a metre of powder and half a metre of packed snow are very different. |
snow_line_altitude | m | The altitude above which precipitation falls as snow. |
freezing_level_altitude | m | Height 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#
meteo1 creditWave 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#
meteo1 creditRiver 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]
}
}
| Field | Meaning |
|---|---|
river_discharge | Mean 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_index | Catchment 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.