Open-meteo offers a diverse range of APIs that will suit user’s weather-related needs:
1. ECMWF Weather Forecast API¶
!pip3 install openmeteo-requests
!pip3 install requests-cache retry-requests numpy pandas
import openmeteo_requests
import pandas as pd
import requests_cache
from retry_requests import retry
# Setup the Open-Meteo API client with cache and retry on error
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
openmeteo = openmeteo_requests.Client(session = retry_session)
# Make sure all required weather variables are listed here
# The order of variables in hourly or daily is important to assign them correctly below
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": 52.52,
"longitude": 13.41,
"hourly": "temperature_2m",
"models": "ecmwf_ifs025"
}
responses = openmeteo.weather_api(url, params=params)
# Process first location. Add a for-loop for multiple locations or weather models
response = responses[0]
model = "IFS" if response.Model() == 60 else "AIFS"
print(f"ECMWF {model} model")
print(f"Coordinates {response.Latitude()}°N {response.Longitude()}°E")
print(f"Elevation {response.Elevation()} m asl")
print(f"Timezone {response.Timezone()}{response.TimezoneAbbreviation()}")
print(f"Timezone difference to GMT+0 {response.UtcOffsetSeconds()} s")
# Process hourly data. The order of variables needs to be the same as requested.
hourly = response.Hourly()
hourly_temperature_2m = hourly.Variables(0).ValuesAsNumpy()
hourly_data = {"date": pd.date_range(
start = pd.to_datetime(hourly.Time(), unit = "s", utc = True),
end = pd.to_datetime(hourly.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = hourly.Interval()),
inclusive = "left"
)}
hourly_data["temperature_2m"] = hourly_temperature_2m
hourly_dataframe = pd.DataFrame(data = hourly_data)
hourly_dataframe.to_csv(f'{model}_lat{response.Latitude()}-lon{response.Longitude()}_hourly.csv', index=False)
print(hourly_dataframe)
ECMWF IFS model
Coordinates 52.5°N 13.5°E
Elevation 38.0 m asl
Timezone NoneNone
Timezone difference to GMT+0 0 s
date temperature_2m
0 2025-06-20 00:00:00+00:00 13.850000
1 2025-06-20 01:00:00+00:00 12.800000
2 2025-06-20 02:00:00+00:00 11.800000
3 2025-06-20 03:00:00+00:00 11.450000
4 2025-06-20 04:00:00+00:00 12.000000
.. ... ...
163 2025-06-26 19:00:00+00:00 26.250000
164 2025-06-26 20:00:00+00:00 25.450001
165 2025-06-26 21:00:00+00:00 24.700001
166 2025-06-26 22:00:00+00:00 23.950001
167 2025-06-26 23:00:00+00:00 23.250000
[168 rows x 2 columns]
2. Forecast API¶
It allows you to access accurate weather forecasts for up to 16 days.
Example of three weather variables and two models for different locations
import openmeteo_requests
import pandas as pd
import requests_cache
from retry_requests import retry
# Setup the Open-Meteo API client with cache and retry on error
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
openmeteo = openmeteo_requests.Client(session = retry_session)
# Make sure all required weather variables are listed here
# The order of variables in hourly or daily is important to assign them correctly below
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": [51.51, 55.95, 53.35],
"longitude": [0.13, 3.19, 6.26],
"daily": ["rain_sum", "precipitation_sum", "cloud_cover_mean"],
"models": ["ecmwf_ifs025", "ecmwf_aifs025_single"],
"timezone": "auto",
"start_date": "2025-06-10",
"end_date": "2025-06-17"
}
responses = openmeteo.weather_api(url, params=params)
# Process first location. Add a for-loop for multiple locations or weather models
for r in range(len(responses)):
response = responses[r]
model = "IFS" if response.Model() == 60 else "AIFS"
print(f"ECMWF {model} model")
print(f"Coordinates {response.Latitude()}°N {response.Longitude()}°E")
print(f"Elevation {response.Elevation()} m asl")
print(f"Timezone {response.Timezone()}{response.TimezoneAbbreviation()}")
print(f"Timezone difference to GMT+0 {response.UtcOffsetSeconds()} s")
# Process daily data. The order of variables needs to be the same as requested.
daily = response.Daily()
daily_rain_sum = daily.Variables(0).ValuesAsNumpy()
daily_precipitation_sum = daily.Variables(1).ValuesAsNumpy()
daily_cloud_cover_mean = daily.Variables(2).ValuesAsNumpy()
daily_data = {"date": pd.date_range(
start = pd.to_datetime(daily.Time(), unit = "s", utc = True),
end = pd.to_datetime(daily.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = daily.Interval()),
inclusive = "left"
)}
daily_data["rain_sum"] = daily_rain_sum
daily_data["precipitation_sum"] = daily_precipitation_sum
daily_data["cloud_cover_mean"] = daily_cloud_cover_mean
daily_dataframe = pd.DataFrame(data = daily_data)
daily_dataframe.to_csv(f'{model}_lat{response.Latitude()}-lon{response.Longitude()}_daily.csv', index=False)
print(daily_dataframe)
Table 1:The Forecast API provides you with data of ECMWF IFS and AIFS models.
Forecast Data Provider | Model | Resolution | Forecast Length | Frequency |
---|---|---|---|---|
ECMWF | IFS & AIFS | 25 km | 15 days | Every 6 hours |
3. Historical Weather API¶
It provides access to archived ECMWF IFS model data from 2017 onwards or ECMWF IFS Assimilation Long-Window model data that are available since 2024.
Historical Data - IFS model
An example of retrieving open data from ECMWF IFS model for two sites: Port Elizabeth (33.91˚S, 25.58 °E) and East London (33.02˚S, 27.91°E), for year 2017. The selected daily weather variables are Maximum Wind Speed (10 m), Maximum Wind Gust (10 m), Mean Wind Gusts (10 m), and Mean Wind Speed (10 m). For hourly weather variables, we chose Wind Speed (10 m), Wind Speed (100 m), and Wind Gusts (10 m).
import openmeteo_requests
import pandas as pd
import requests_cache
from retry_requests import retry
# Setup the Open-Meteo API client with cache and retry on error
cache_session = requests_cache.CachedSession('.cache', expire_after = -1)
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
openmeteo = openmeteo_requests.Client(session = retry_session)
# Make sure all required weather variables are listed here
# The order of variables in hourly or daily is important to assign them correctly below
url = "https://archive-api.open-meteo.com/v1/archive"
params = {
"latitude": [33.91, 33.02],
"longitude": [25.58, 27.91],
"start_date": "2017-01-01",
"end_date": "2017-12-31",
"daily": ["wind_speed_10m_max", "wind_gusts_10m_max", "wind_gusts_10m_mean", "wind_speed_10m_mean"],
"hourly": ["wind_speed_10m", "wind_gusts_10m", "wind_speed_100m"],
"models": "ecmwf_ifs",
"timezone": "auto"
}
responses = openmeteo.weather_api(url, params=params)
# Process first location. Add a for-loop for multiple locations or weather models
for r in range(len(responses)):
response = responses[r]
model = "IFS" if response.Model() == 60 else "AIFS"
print(f"Coordinates {response.Latitude()}°N {response.Longitude()}°E")
print(f"Elevation {response.Elevation()} m asl")
print(f"Timezone {response.Timezone()}{response.TimezoneAbbreviation()}")
print(f"Timezone difference to GMT+0 {response.UtcOffsetSeconds()} s")
# Process hourly data. The order of variables needs to be the same as requested.
hourly = response.Hourly()
hourly_wind_speed_10m = hourly.Variables(0).ValuesAsNumpy()
hourly_wind_gusts_10m = hourly.Variables(1).ValuesAsNumpy()
hourly_wind_speed_100m = hourly.Variables(2).ValuesAsNumpy()
hourly_data = {"date": pd.date_range(
start = pd.to_datetime(hourly.Time(), unit = "s", utc = True),
end = pd.to_datetime(hourly.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = hourly.Interval()),
inclusive = "left"
)}
hourly_data["wind_speed_10m"] = hourly_wind_speed_10m
hourly_data["wind_gusts_10m"] = hourly_wind_gusts_10m
hourly_data["wind_speed_100m"] = hourly_wind_speed_100m
hourly_dataframe = pd.DataFrame(data = hourly_data)
hourly_dataframe.to_csv(f'{model}_lat{response.Latitude()}-lon{response.Longitude()}_hourly.csv', index=False)
print(hourly_dataframe)
# Process daily data. The order of variables needs to be the same as requested.
daily = response.Daily()
daily_wind_speed_10m_max = daily.Variables(0).ValuesAsNumpy()
daily_wind_gusts_10m_max = daily.Variables(1).ValuesAsNumpy()
daily_wind_gusts_10m_mean = daily.Variables(2).ValuesAsNumpy()
daily_wind_speed_10m_mean = daily.Variables(3).ValuesAsNumpy()
daily_data = {"date": pd.date_range(
start = pd.to_datetime(daily.Time(), unit = "s", utc = True),
end = pd.to_datetime(daily.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = daily.Interval()),
inclusive = "left"
)}
daily_data["wind_speed_10m_max"] = daily_wind_speed_10m_max
daily_data["wind_gusts_10m_max"] = daily_wind_gusts_10m_max
daily_data["wind_gusts_10m_mean"] = daily_wind_gusts_10m_mean
daily_data["wind_speed_10m_mean"] = daily_wind_speed_10m_mean
daily_dataframe = pd.DataFrame(data = daily_data)
daily_dataframe.to_csv(f'{model}_lat{response.Latitude()}-lon{response.Longitude()}_daily.csv', index=False)
print(daily_dataframe)
Table 2:The Historical Weather API allows users to retrieve historical weather data of ECMWF IFS (using simulation runs at 00z and 12z) and IFS Assimilation Long-Window models for a specific location and time period.
Forecast Data Provider | Model | Spatial Resolution | Temporal Resolution | Frequency | Available Since |
---|---|---|---|---|---|
ECMWF | IFS | 9 km | hourly | daily with 2 days delay | 2017 to present |
ECMWF | IFS Assimilation Long-Window | 9 km | 6-hourly | daily with 2 days delay | 2024 to present |
Historical Forecast API provides access to archived model data from the ECMWF Weather Forecast API.
Historical Data - IFS & AIFS
An example of retrieving open data from ECMWF IFS and AIFS models for Bern (46.95° N, 7.45° E) from 2024-02-03
to 2025-06-15
. The selected daily weather variables are Maximum Temperature (2 m), Minimum Temperature (2 m), and Mean Temperature (2 m). For hourly weather variables, we chose Temperature (2 m), Rain, and Snowfall.
import openmeteo_requests
import pandas as pd
import requests_cache
from retry_requests import retry
# Setup the Open-Meteo API client with cache and retry on error
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
openmeteo = openmeteo_requests.Client(session = retry_session)
# Make sure all required weather variables are listed here
# The order of variables in hourly or daily is important to assign them correctly below
url = "https://historical-forecast-api.open-meteo.com/v1/forecast"
params = {
"latitude": 46.95,
"longitude": 7.45,
"start_date": "2024-02-03",
"end_date": "2025-06-15",
"daily": ["temperature_2m_max", "temperature_2m_mean", "temperature_2m_min"],
"hourly": ["rain", "snowfall", "temperature_2m"],
"models": ["ecmwf_ifs025", "ecmwf_aifs025_single"],
"timezone": "auto"
}
responses = openmeteo.weather_api(url, params=params)
# Process first location. Add a for-loop for multiple locations or weather models
for r in range(len(responses)):
response = responses[r]
model = "IFS" if response.Model() == 60 else "AIFS"
print(f"ECMWF {model} model")
print(f"Coordinates {response.Latitude()}°N {response.Longitude()}°E")
print(f"Elevation {response.Elevation()} m asl")
print(f"Timezone {response.Timezone()}{response.TimezoneAbbreviation()}")
print(f"Timezone difference to GMT+0 {response.UtcOffsetSeconds()} s")
# Process hourly data. The order of variables needs to be the same as requested.
hourly = response.Hourly()
hourly_rain = hourly.Variables(0).ValuesAsNumpy()
hourly_snowfall = hourly.Variables(1).ValuesAsNumpy()
hourly_temperature_2m = hourly.Variables(2).ValuesAsNumpy()
hourly_data = {"date": pd.date_range(
start = pd.to_datetime(hourly.Time(), unit = "s", utc = True),
end = pd.to_datetime(hourly.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = hourly.Interval()),
inclusive = "left"
)}
hourly_data["rain"] = hourly_rain
hourly_data["snowfall"] = hourly_snowfall
hourly_data["temperature_2m"] = hourly_temperature_2m
hourly_dataframe = pd.DataFrame(data = hourly_data)
hourly_dataframe.to_csv(f'{model}_lat{response.Latitude()}-lon{response.Longitude()}_hourly.csv', index=False)
print(hourly_dataframe)
# Process daily data. The order of variables needs to be the same as requested.
daily = response.Daily()
daily_temperature_2m_max = daily.Variables(0).ValuesAsNumpy()
daily_temperature_2m_mean = daily.Variables(1).ValuesAsNumpy()
daily_temperature_2m_min = daily.Variables(2).ValuesAsNumpy()
daily_data = {"date": pd.date_range(
start = pd.to_datetime(daily.Time(), unit = "s", utc = True),
end = pd.to_datetime(daily.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = daily.Interval()),
inclusive = "left"
)}
daily_data["temperature_2m_max"] = daily_temperature_2m_max
daily_data["temperature_2m_mean"] = daily_temperature_2m_mean
daily_data["temperature_2m_min"] = daily_temperature_2m_min
daily_dataframe = pd.DataFrame(data = daily_data)
daily_dataframe.to_csv(f'{model}_lat{response.Latitude()}-lon{response.Longitude()}_daily.csv', index=False)
print(daily_dataframe)
Table 3:The Historical Forecast API archives comprehensive data of ECMWF IFS and AIFS models, data is available since 2022.
Forecast Data Provider | Model | Spatial Resolution | Temporal Resolution | Frequency | Available Since |
---|---|---|---|---|---|
IFS 0.4° | 0.4° (~44 km) | 3-hourly | 2022-11-07 | ||
ECMWF | IFS 0.25° | 0.25° (~25 km) | 3-hourly | every 6 hours | 2024-02-03 |
AIFS 0.25° Single | 0.25° (~25 km) | 6-hourly | 2025-02-25 |
4. Ensemble Models API¶
ECMWF IFS 0.25° Ensemble
An example shows how to retrieve weather data of daily mean temperature (2 m) and daily mean wind speed (10 m) in Berlin in the past 3 months.
https://ensemble-api.open-meteo.com/v1/ensemble?latitude=52.52&longitude=13.41&daily=temperature_2m_mean,wind_speed_10m_mean&models=ecmwf_ifs025&timezone=Europe%2FBerlin&past_days=92
import openmeteo_requests
from openmeteo_sdk.Variable import Variable
from openmeteo_sdk.Aggregation import Aggregation
import pandas as pd
import requests_cache
from retry_requests import retry
# Setup the Open-Meteo API client with cache and retry on error
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
openmeteo = openmeteo_requests.Client(session = retry_session)
# Make sure all required weather variables are listed here
# The order of variables in hourly or daily is important to assign them correctly below
url = "https://ensemble-api.open-meteo.com/v1/ensemble"
params = {
"latitude": 52.52,
"longitude": 13.41,
"daily": ["temperature_2m_mean", "wind_speed_10m_mean"],
"models": "ecmwf_ifs025",
"timezone": "Europe/Berlin",
"past_days": 92
}
responses = openmeteo.weather_api(url, params=params)
# Process first location. Add a for-loop for multiple locations or weather models
response = responses[0]
model = "IFS" if response.Model() == 60 else "AIFS"
print(f"ECMWF {model} model")
print(f"Coordinates {response.Latitude()}°N {response.Longitude()}°E")
print(f"Elevation {response.Elevation()} m asl")
print(f"Timezone {response.Timezone()}{response.TimezoneAbbreviation()}")
print(f"Timezone difference to GMT+0 {response.UtcOffsetSeconds()} s")
# Process daily data
daily = response.Daily()
daily_variables = list(map(lambda i: daily.Variables(i), range(0, daily.VariablesLength())))
daily_temperature_2m_mean = filter(lambda x: x.Variable() == Variable.temperature and x.Altitude() == 2 and x.Aggregation() == Aggregation.mean, daily_variables)
daily_wind_speed_10m_mean = filter(lambda x: x.Variable() == Variable.wind_speed and x.Altitude() == 10 and x.Aggregation() == Aggregation.mean, daily_variables)
daily_data = {"date": pd.date_range(
start = pd.to_datetime(daily.Time(), unit = "s", utc = True),
end = pd.to_datetime(daily.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = daily.Interval()),
inclusive = "left"
)}
# Process all members
for variable in daily_temperature_2m_mean:
member = variable.EnsembleMember()
daily_data[f"temperature_2m_mean_member{member}"] = variable.ValuesAsNumpy()
for variable in daily_wind_speed_10m_mean:
member = variable.EnsembleMember()
daily_data[f"wind_speed_10m_mean_member{member}"] = variable.ValuesAsNumpy()
daily_dataframe = pd.DataFrame(data = daily_data)
daily_dataframe.to_csv(f'{model}_lat{response.Latitude()}-lon{response.Longitude()}_daily.csv', index=False)
print(daily_dataframe)