Skip to article frontmatterSkip to article content

Open-meteo


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 ProviderModelResolutionForecast LengthFrequency
ECMWFIFS & AIFS25 km15 daysEvery 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 ProviderModelSpatial ResolutionTemporal ResolutionFrequencyAvailable Since
ECMWFIFS9 kmhourlydaily with 2 days delay2017 to present
ECMWFIFS Assimilation Long-Window9 km6-hourlydaily with 2 days delay2024 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 ProviderModelSpatial ResolutionTemporal ResolutionFrequencyAvailable Since
IFS 0.4°0.4° (~44 km)3-hourly2022-11-07
ECMWFIFS 0.25°0.25° (~25 km)3-hourlyevery 6 hours2024-02-03
AIFS 0.25° Single0.25° (~25 km)6-hourly2025-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)