DEV Community

KartikJha-prod
KartikJha-prod

Posted on

How to get Weather-Data in Python using Open Meteo API.

Open-Meteo is a free API that is used to get weather data. It doesn't need a key or even a sign-up, allowing you to run your first request in a snap of your fingers. In this guide I will try to explain this API purely from a Python developer's viewpoint, and I will only cover the two major endpoints:

• Geocoding - Used to convert the names of cities to their coordinates on a world map.

• Forecast - The expected weather data for that location.

Prerequisites: Python 3 installed and basic knowledge of Python.

Usage terms: Open-Meteo is free for non-commercial use, and the data is licensed CC BY 4.0, so you must credit Open-Meteo. Check the official site for current rate limits and commercial terms.


Set-up

For the set-up you will first need to install the requests Python library by running this in your terminal.

pip install requests
Enter fullscreen mode Exit fullscreen mode

Starting Out

To start, let me provide you with the code to get the temperature in, say, Berlin, Germany. It is as follows:

import requests

response = requests.get(
    "https://api.open-meteo.com/v1/forecast",
    params={
        "latitude": 52.52,
        "longitude": 13.41,
        "current": "temperature_2m",
    },
)

data = response.json()
print(data["current"]["temperature_2m"])
Enter fullscreen mode Exit fullscreen mode

Save this code in a file such as weather.py and run it in your terminal with python weather.py after installing the requests library. Here is what has been done in this code:

1) We have imported the requests library to get data from an API.

2) We have response, which is just a variable that holds the API's reply, set equal to the following:

2.1) Get data from the API website using the API link.

2.2) Set the params (parameters) as the latitude and longitude of the place. In this case they are the coordinates of Berlin. Then we ask for the current temperature at 2 metres above ground level (that is what 2m means).

3) Now we close the parameters and the response query.

4) Then we convert the response to a Python dictionary with .json() and print the current temperature value.


Authentication

None for free, non-commercial use, as requests are plain GET calls with no headers or keys. (Commercial subscription tiers use an API key.)


Endpoint 1: Geocoding

We will find exact locations from just the city names. Here is the base URL.

https://geocoding-api.open-meteo.com/v1/search
Enter fullscreen mode Exit fullscreen mode

Here are the parameters:

Parameter Type Required Description
name string Yes Name of the city you want to check.
count integer No Number of results you want (Default 10).
language string No The language you want the result in. Default (en for English).
format string No The data format of the response default is json.
countryCode string No Filters countries using its two letter code.
apikey string No Authentication key required only for commercial subscription tiers.

Here is an example that looks up the location data for London:

import requests

response = requests.get(
    "https://geocoding-api.open-meteo.com/v1/search",
    params={"name": "London", "count": 1, "language": "en"},
)

data = response.json()
print(data["results"][0])
Enter fullscreen mode Exit fullscreen mode

• You start by importing the requests module.
• You set up the variable response with the URL as the base URL I provided above.
• Then you enter the parameters. Only the name is required, everything else is optional.


The response will be something like this:

{
  "results": [
    {
      "id": 2643743,
      "name": "London",
      "latitude": 51.50853,
      "longitude": -0.12574,
      "elevation": 25.0,
      "feature_code": "PPLC",
      "country_code": "GB",
      "admin1_id": 6269131,
      "admin2_id": 2648110,
      "timezone": "Europe/London",
      "population": 8961989,
      "country_id": 2635167,
      "country": "United Kingdom",
      "admin1": "England",
      "admin2": "Greater London"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Here is what these response fields mean.

Attribute Names Description Values
id Unique identifier for the geoname location entity 2643743
name Name of the specific city or location London
latitude Geographic coordinate specifying north-south position 51.50853
longitude Geographic coordinate specifying east-west position -0.12574
elevation Height above sea level measured in metres 25.0
feature_code Classification code mapping the location type PPLC
country_code Two-letter ISO country code for the nation GB
admin1_id Unique ID for the primary administrative division 6269131
admin2_id Unique ID for the secondary administrative division 2648110
timezone Which region is the location in. Europe/London
population Total number of people living in the area 8961989
country_id Unique identifier for the country 2635167
country Full name of the country United Kingdom
admin1 Name of the primary administrative level (e.g. state/province) England
admin2 Name of the secondary administrative level (e.g. county) Greater London

Note: If nothing matches, the response has no results key at all. Use data.get("results") instead of data["results"] to avoid a KeyError.


Endpoint 2: Forecast

Returns the forecast of conditions for the location.

Base URL:

https://api.open-meteo.com/v1/forecast
Enter fullscreen mode Exit fullscreen mode

Again, these are the parameters for this endpoint.

Parameter Type Required Description
latitude float Yes Latitude in decimal degrees (-90 to 90)
longitude float Yes Longitude in decimal degrees (-180 to 180)
current string No Current-condition variables, comma separated
hourly string No Hourly variables, comma separated
daily string No Daily summaries, comma separated. Requires timezone
timezone string No e.g. Asia/Kolkata or auto
temperature_unit string No celsius (default) or fahrenheit
forecast_days integer No Number of forecast days to return

Now here are some common variable names that you may encounter in this endpoint.

Variable Used in Meaning
temperature_2m current, hourly Air temperature 2 m above ground
relative_humidity_2m current, hourly Relative humidity (%)
wind_speed_10m current, hourly Wind speed 10 m above ground
weather_code current, hourly, daily Weather condition as a WMO code
temperature_2m_max / _min daily Daily highest and lowest temperature
precipitation_sum daily Total daily precipitation (mm)

Here is an example:

import requests

response = requests.get(
    "https://api.open-meteo.com/v1/forecast",
    params={
        "latitude": 19.07,
        "longitude": 72.88,
        "current": "temperature_2m,wind_speed_10m",
        "daily": "temperature_2m_max,temperature_2m_min",
        "timezone": "Asia/Kolkata",
        "forecast_days": 3,
    },
)

data = response.json()
print(data)
Enter fullscreen mode Exit fullscreen mode

Here's how this works:

• You first import the requests module like always.

• You again set up a variable that gets its data from the base URL I provided above. Make sure it is the URL of the 2nd endpoint and not the 1st.

• You then define the parameters. Only the latitude and longitude of your chosen city are required. I chose Mumbai, India, so I have entered its coordinates.

• Then you enter additional parameters and variables using the above table as a reference.

• Then you convert the response with response.json(), store it in the variable data and print it.


You will see something like this if you did it correctly (the times and values will differ, since this is live data):

{
  "latitude": 19.07,
  "longitude": 72.88,
  "timezone": "Asia/Kolkata",
  "current_units": {
    "temperature_2m": "°C",
    "wind_speed_10m": "km/h"
  },
  "current": {
    "time": "2026-10-23T14:00",
    "temperature_2m": 32.1,
    "wind_speed_10m": 11.4
  },
  "daily": {
    "time": ["2026-10-23", "2026-10-24", "2026-10-25"],
    "temperature_2m_max": [33.5, 33.2, 32.9],
    "temperature_2m_min": [25.1, 24.8, 25.0]
  }
}
Enter fullscreen mode Exit fullscreen mode

This is a trimmed version by the way (btw). Python's print will show it as a dictionary with single quotes, which is the same data.


In Python this becomes a dictionary. Each key under daily holds a list, and the lists line up by position. Index 0 of temperature_2m_max belongs to the first date in time:

daily = data["daily"]

for date, high, low in zip(
    daily["time"], daily["temperature_2m_max"], daily["temperature_2m_min"]
):
    print(f"{date}: high {high}, low {low}")
Enter fullscreen mode Exit fullscreen mode

Errors

When a request is invalid, the API returns HTTP status 400 and a JSON body explaining why.
Here is an example of a script that will provide an error.

import requests

response = requests.get(
    "https://api.open-meteo.com/v1/forecast",
    params={"latitude": 120, "longitude": 72.88, "current": "temperature_2m"},
)

print(response.status_code)
print(response.json())
Enter fullscreen mode Exit fullscreen mode

The reason for the error here is that latitudes go from -90° to 90° (longitudes go from -180° to 180°), so 120° is an invalid latitude. The error will look something like this:

{
  "error": true,
  "reason": "Latitude must be in range of -90 to 90°. Given: 120.0."
}
Enter fullscreen mode Exit fullscreen mode

Here are some common errors, their codes and fixes:

Status Cause Fix
400 Invalid or missing parameter, or unknown variable name Read the reason field and correct the request
429 Too many requests Slow down and retry later
5xx Server problem Retry after a short wait

These were some common errors. You can refer to the official Open-Meteo documentation or the internet if you have an error that doesn't match the ones provided.


One Final Example: Get the Forecast from a City Name Directly

In this part you will learn to connect both endpoints to get the forecast by just the city name instead of latitudes and longitudes.

import requests

GEOCODING_URL = "https://geocoding-api.open-meteo.com/v1/search"
FORECAST_URL = "https://api.open-meteo.com/v1/forecast"


def get_coordinates(city):
    """Turn a city name into a place dictionary, or None if not found."""
    response = requests.get(GEOCODING_URL, params={"name": city, "count": 1})
    response.raise_for_status()
    results = response.json().get("results")
    return results[0] if results else None


def get_current_weather(latitude, longitude):
    """Fetch current temperature and wind speed for a location."""
    response = requests.get(
        FORECAST_URL,
        params={
            "latitude": latitude,
            "longitude": longitude,
            "current": "temperature_2m,wind_speed_10m",
            "timezone": "auto",
        },
    )
    response.raise_for_status()
    return response.json()


place = get_coordinates("Mumbai")

if place is None:
    print("City not found.")
else:
    weather = get_current_weather(place["latitude"], place["longitude"])
    current = weather["current"]
    print(f"{place['name']}, {place['country']}")
    print(f"Temperature: {current['temperature_2m']} °C")
    print(f"Wind speed: {current['wind_speed_10m']} km/h")
Enter fullscreen mode Exit fullscreen mode

Now here is what has been done.

• You first import the required modules like always and provide the URLs for the two endpoints.

• Now you make a function to get the coordinates of a city from its name, as coordinates are used in the forecast.

• In that function you use requests.get() to get data on the entered city, and return the first result. If the request failed, raise_for_status() raises an exception.

• You now make a second function to get the forecast of the city using endpoint 2.

• You again set response equal to requests.get() from the base URL of the 2nd endpoint, not the first, with the latitude and longitude passed in and the other parameters of your choice.

• You then get the response and return it.

• You then call the get_coordinates() function and store the result in a variable place, with the city name in the brackets.

• You then check if place is empty using an if place is None clause and print an error.

• If the place isn't None, you use the second function you created to print the forecast of the selected city.

Here is how the output can look:

Mumbai, India
Temperature: 32.1 °C
Wind speed: 11.4 km/h
Enter fullscreen mode Exit fullscreen mode

Tips

• Set timezone (or use auto) whenever you request daily data, or the request will fail.
• Pass multiple variables as one comma-separated string, with no spaces.
• Check current_units and daily_units in the response so you know what units you're reading.
• Use response.raise_for_status() so failed requests raise an exception instead of passing silently.
• Cache results instead of calling the API repeatedly for the same location.

Weather data by Open-Meteo.com, licensed CC BY 4.0.

Top comments (0)