Demonstration of API using OpenWeatherMap.org#

This demonstration is heavily inspired by NeuralNine’s video.
You need a free account from here.
The VS Code extension JSON viewer is recommended for viewing downloaded JSON content.

  • Set your maximum API calls to 1000 per day to make sure you are under the limit for billing.

  • To run the examples, download your API key, save it in the right folder (see below) in a file called api_key_OpenWeather, containing only the key (no spaces or “enters”).

# Imports
import datetime as dt
import requests
import json

Current weather#

Common definitions to use for all requests#

BASE_URL = "http://api.openweathermap.org/data/2.5/weather?"
API_KEY = open('../../../No_sync/api_key_OpenWeather','r').read()
CITY = "Ski" # Implicit geocoding is deprecated.

url = BASE_URL + "q=" + CITY + "&appid=" + API_KEY

Request current weather in chosen city#

response = requests.get(url).json()
print(response)
{'coord': {'lon': 10.8358, 'lat': 59.7195}, 'weather': [{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10n'}], 'base': 'stations', 'main': {'temp': 284.14, 'feels_like': 283.91, 'temp_min': 284.14, 'temp_max': 284.44, 'pressure': 1010, 'humidity': 100, 'sea_level': 1010, 'grnd_level': 993}, 'visibility': 10000, 'wind': {'speed': 2.59, 'deg': 189, 'gust': 7.21}, 'rain': {'1h': 0.13}, 'clouds': {'all': 100}, 'dt': 1762372526, 'sys': {'type': 2, 'id': 2006772, 'country': 'NO', 'sunrise': 1762325266, 'sunset': 1762355562}, 'timezone': 3600, 'id': 3139081, 'name': 'Ski', 'cod': 200}
# Write JSON to file for viewing
with open('downloads/weather.json', 'w') as f:
    json.dump(response, f, indent=4)

Conversion functions#

Changing scales can make results more interpretable

# Kelvin to Celsius
def kelvin_to_celsius(temp):
    return temp - 273.15

# Meters per second to knots
def mps_to_knots(speed):
    return speed * 1.943844

Forecasted weather#

Common definitions to use for all requests#

BASE_URL = "https://api.openweathermap.org/data/2.5/forecast?"
CITY = "Agadir"

urlF = BASE_URL + "q=" + CITY + "&appid=" + API_KEY

Request forecasted weather in chosen city#

responseF = requests.get(urlF).json()
#print(json.dumps(responseF, indent=4))
print(responseF)
{'cod': '200', 'message': 0, 'cnt': 40, 'list': [{'dt': 1762376400, 'main': {'temp': 294.4, 'feels_like': 294.3, 'temp_min': 293.81, 'temp_max': 294.4, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1002, 'humidity': 66, 'temp_kf': 0.59}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 2}, 'wind': {'speed': 1.79, 'deg': 323, 'gust': 3.28}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-05 21:00:00'}, {'dt': 1762387200, 'main': {'temp': 293.98, 'feels_like': 293.87, 'temp_min': 293.15, 'temp_max': 293.98, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1001, 'humidity': 67, 'temp_kf': 0.83}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 2}, 'wind': {'speed': 1.09, 'deg': 96, 'gust': 1.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-06 00:00:00'}, {'dt': 1762398000, 'main': {'temp': 292.75, 'feels_like': 292.59, 'temp_min': 291.93, 'temp_max': 292.75, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1000, 'humidity': 70, 'temp_kf': 0.82}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 6}, 'wind': {'speed': 1.95, 'deg': 79, 'gust': 1.76}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-06 03:00:00'}, {'dt': 1762408800, 'main': {'temp': 291.19, 'feels_like': 291.14, 'temp_min': 291.19, 'temp_max': 291.19, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 80, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02n'}], 'clouds': {'all': 17}, 'wind': {'speed': 2.32, 'deg': 110, 'gust': 1.79}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-06 06:00:00'}, {'dt': 1762419600, 'main': {'temp': 292.82, 'feels_like': 292.8, 'temp_min': 292.82, 'temp_max': 292.82, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 75, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 63}, 'wind': {'speed': 1.7, 'deg': 136, 'gust': 1.28}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-06 09:00:00'}, {'dt': 1762430400, 'main': {'temp': 294.87, 'feels_like': 294.85, 'temp_min': 294.87, 'temp_max': 294.87, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 54}, 'wind': {'speed': 3.54, 'deg': 269, 'gust': 2.7}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-06 12:00:00'}, {'dt': 1762441200, 'main': {'temp': 296.16, 'feels_like': 296, 'temp_min': 296.16, 'temp_max': 296.16, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 998, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 95}, 'wind': {'speed': 4.64, 'deg': 288, 'gust': 4.68}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-06 15:00:00'}, {'dt': 1762452000, 'main': {'temp': 293.77, 'feels_like': 293.58, 'temp_min': 293.77, 'temp_max': 293.77, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}], 'clouds': {'all': 70}, 'wind': {'speed': 3.89, 'deg': 321, 'gust': 5.6}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-06 18:00:00'}, {'dt': 1762462800, 'main': {'temp': 291.82, 'feels_like': 291.73, 'temp_min': 291.82, 'temp_max': 291.82, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 76, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 48}, 'wind': {'speed': 4.65, 'deg': 106, 'gust': 4.43}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-06 21:00:00'}, {'dt': 1762473600, 'main': {'temp': 290.08, 'feels_like': 290.05, 'temp_min': 290.08, 'temp_max': 290.08, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 85, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 30}, 'wind': {'speed': 3.9, 'deg': 112, 'gust': 3.62}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-07 00:00:00'}, {'dt': 1762484400, 'main': {'temp': 289.5, 'feels_like': 289.36, 'temp_min': 289.5, 'temp_max': 289.5, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 83, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02n'}], 'clouds': {'all': 20}, 'wind': {'speed': 2.26, 'deg': 87, 'gust': 2.33}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-07 03:00:00'}, {'dt': 1762495200, 'main': {'temp': 289.26, 'feels_like': 289.07, 'temp_min': 289.26, 'temp_max': 289.26, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 998, 'humidity': 82, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02n'}], 'clouds': {'all': 20}, 'wind': {'speed': 0.83, 'deg': 44, 'gust': 1.69}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-07 06:00:00'}, {'dt': 1762506000, 'main': {'temp': 290.86, 'feels_like': 290.67, 'temp_min': 290.86, 'temp_max': 290.86, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 76, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 74}, 'wind': {'speed': 1.45, 'deg': 168, 'gust': 1.03}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-07 09:00:00'}, {'dt': 1762516800, 'main': {'temp': 293.06, 'feels_like': 292.78, 'temp_min': 293.06, 'temp_max': 293.06, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 64, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 44}, 'wind': {'speed': 2.92, 'deg': 242, 'gust': 1.88}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-07 12:00:00'}, {'dt': 1762527600, 'main': {'temp': 294.44, 'feels_like': 294.14, 'temp_min': 294.44, 'temp_max': 294.44, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 71}, 'wind': {'speed': 2.06, 'deg': 203, 'gust': 2.17}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-07 15:00:00'}, {'dt': 1762538400, 'main': {'temp': 292.53, 'feels_like': 292.3, 'temp_min': 292.53, 'temp_max': 292.53, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 996, 'humidity': 68, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 43}, 'wind': {'speed': 3.29, 'deg': 130, 'gust': 1.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-07 18:00:00'}, {'dt': 1762549200, 'main': {'temp': 291.41, 'feels_like': 291.2, 'temp_min': 291.41, 'temp_max': 291.41, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 73, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.46, 'deg': 129, 'gust': 4.27}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-07 21:00:00'}, {'dt': 1762560000, 'main': {'temp': 290.21, 'feels_like': 290.01, 'temp_min': 290.21, 'temp_max': 290.21, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 78, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.66, 'deg': 148, 'gust': 1.6}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-08 00:00:00'}, {'dt': 1762570800, 'main': {'temp': 289.96, 'feels_like': 289.6, 'temp_min': 289.96, 'temp_max': 289.96, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 997, 'humidity': 73, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.43, 'deg': 237, 'gust': 0.86}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-08 03:00:00'}, {'dt': 1762581600, 'main': {'temp': 289.68, 'feels_like': 289.4, 'temp_min': 289.68, 'temp_max': 289.68, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 997, 'humidity': 77, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.3, 'deg': 245, 'gust': 0.85}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-08 06:00:00'}, {'dt': 1762592400, 'main': {'temp': 292.02, 'feels_like': 291.79, 'temp_min': 292.02, 'temp_max': 292.02, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.67, 'deg': 165, 'gust': 1.92}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-08 09:00:00'}, {'dt': 1762603200, 'main': {'temp': 294.7, 'feels_like': 294.5, 'temp_min': 294.7, 'temp_max': 294.7, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 998, 'humidity': 61, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.2, 'deg': 255, 'gust': 2.58}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-08 12:00:00'}, {'dt': 1762614000, 'main': {'temp': 295.15, 'feels_like': 294.89, 'temp_min': 295.15, 'temp_max': 295.15, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 996, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.77, 'deg': 263, 'gust': 4.25}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-08 15:00:00'}, {'dt': 1762624800, 'main': {'temp': 291.8, 'feels_like': 291.68, 'temp_min': 291.8, 'temp_max': 291.8, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 997, 'humidity': 75, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.12, 'deg': 179, 'gust': 2.43}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-08 18:00:00'}, {'dt': 1762635600, 'main': {'temp': 292.1, 'feels_like': 291.85, 'temp_min': 292.1, 'temp_max': 292.1, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.52, 'deg': 112, 'gust': 5.38}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-08 21:00:00'}, {'dt': 1762646400, 'main': {'temp': 292.54, 'feels_like': 292.1, 'temp_min': 292.54, 'temp_max': 292.54, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 60, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.29, 'deg': 111, 'gust': 4.78}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-09 00:00:00'}, {'dt': 1762657200, 'main': {'temp': 291.16, 'feels_like': 290.84, 'temp_min': 291.16, 'temp_max': 291.16, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 1}, 'wind': {'speed': 2.38, 'deg': 299, 'gust': 2.49}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-09 03:00:00'}, {'dt': 1762668000, 'main': {'temp': 290.81, 'feels_like': 290.64, 'temp_min': 290.81, 'temp_max': 290.81, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 77, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 1}, 'wind': {'speed': 2.51, 'deg': 285, 'gust': 2.63}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-09 06:00:00'}, {'dt': 1762678800, 'main': {'temp': 292.42, 'feels_like': 292.39, 'temp_min': 292.42, 'temp_max': 292.42, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 76, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 6}, 'wind': {'speed': 2.21, 'deg': 243, 'gust': 2.19}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-09 09:00:00'}, {'dt': 1762689600, 'main': {'temp': 294.11, 'feels_like': 294.06, 'temp_min': 294.11, 'temp_max': 294.11, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 26}, 'wind': {'speed': 2.45, 'deg': 225, 'gust': 2.29}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-09 12:00:00'}, {'dt': 1762700400, 'main': {'temp': 295.35, 'feels_like': 295.17, 'temp_min': 295.35, 'temp_max': 295.35, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 59, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 100}, 'wind': {'speed': 4.9, 'deg': 270, 'gust': 4.52}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-09 15:00:00'}, {'dt': 1762711200, 'main': {'temp': 293.13, 'feels_like': 292.91, 'temp_min': 293.13, 'temp_max': 293.13, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 998, 'humidity': 66, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 2, 'deg': 244, 'gust': 1.96}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-09 18:00:00'}, {'dt': 1762722000, 'main': {'temp': 292.32, 'feels_like': 292.22, 'temp_min': 292.32, 'temp_max': 292.32, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 74, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 3.73, 'deg': 136, 'gust': 3.08}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-09 21:00:00'}, {'dt': 1762732800, 'main': {'temp': 291.81, 'feels_like': 291.69, 'temp_min': 291.81, 'temp_max': 291.81, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 75, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 4.66, 'deg': 122, 'gust': 5.08}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-10 00:00:00'}, {'dt': 1762743600, 'main': {'temp': 291.21, 'feels_like': 290.98, 'temp_min': 291.21, 'temp_max': 291.21, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 73, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 99}, 'wind': {'speed': 0.31, 'deg': 212, 'gust': 1.45}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-10 03:00:00'}, {'dt': 1762754400, 'main': {'temp': 290.48, 'feels_like': 290.33, 'temp_min': 290.48, 'temp_max': 290.48, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 79, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 93}, 'wind': {'speed': 1.9, 'deg': 285, 'gust': 1.72}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-10 06:00:00'}, {'dt': 1762765200, 'main': {'temp': 292.9, 'feels_like': 292.71, 'temp_min': 292.9, 'temp_max': 292.9, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 68, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 2}, 'wind': {'speed': 1.83, 'deg': 229, 'gust': 0.87}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-10 09:00:00'}, {'dt': 1762776000, 'main': {'temp': 294.17, 'feels_like': 293.95, 'temp_min': 294.17, 'temp_max': 294.17, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 62, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 1}, 'wind': {'speed': 3.04, 'deg': 230, 'gust': 0.96}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-10 12:00:00'}, {'dt': 1762786800, 'main': {'temp': 295.55, 'feels_like': 295.31, 'temp_min': 295.55, 'temp_max': 295.55, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 56, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.41, 'deg': 260, 'gust': 2.85}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-10 15:00:00'}, {'dt': 1762797600, 'main': {'temp': 292.58, 'feels_like': 292.46, 'temp_min': 292.58, 'temp_max': 292.58, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 72, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 1}, 'wind': {'speed': 1.01, 'deg': 184, 'gust': 1.63}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-10 18:00:00'}], 'city': {'id': 2561668, 'name': 'Agadir', 'coord': {'lat': 30.4202, 'lon': -9.5982}, 'country': 'MA', 'population': 698310, 'timezone': 3600, 'sunrise': 1762325761, 'sunset': 1762364876}}
# Write JSON to file for viewing
with open('downloads/forecast.json', 'w') as f:
    json.dump(responseF, f, indent=4)

When and what?#

Check contents and time stamps

# Content of responseF
responseF.keys()
dict_keys(['cod', 'message', 'cnt', 'list', 'city'])
# Number of forecasts
print(len(responseF["list"]))
40
# Print forecast times
for forecast in responseF["list"]:
    print(forecast["dt_txt"])
2025-11-05 21:00:00
2025-11-06 00:00:00
2025-11-06 03:00:00
2025-11-06 06:00:00
2025-11-06 09:00:00
2025-11-06 12:00:00
2025-11-06 15:00:00
2025-11-06 18:00:00
2025-11-06 21:00:00
2025-11-07 00:00:00
2025-11-07 03:00:00
2025-11-07 06:00:00
2025-11-07 09:00:00
2025-11-07 12:00:00
2025-11-07 15:00:00
2025-11-07 18:00:00
2025-11-07 21:00:00
2025-11-08 00:00:00
2025-11-08 03:00:00
2025-11-08 06:00:00
2025-11-08 09:00:00
2025-11-08 12:00:00
2025-11-08 15:00:00
2025-11-08 18:00:00
2025-11-08 21:00:00
2025-11-09 00:00:00
2025-11-09 03:00:00
2025-11-09 06:00:00
2025-11-09 09:00:00
2025-11-09 12:00:00
2025-11-09 15:00:00
2025-11-09 18:00:00
2025-11-09 21:00:00
2025-11-10 00:00:00
2025-11-10 03:00:00
2025-11-10 06:00:00
2025-11-10 09:00:00
2025-11-10 12:00:00
2025-11-10 15:00:00
2025-11-10 18:00:00

Make plots of omnipresent measurements and events#

We will later look at missing data, data only sporadically appearing and so on.

# Air pressure per period
pressures = []
timestamps = []
for forecast in responseF["list"]:
    pressures.append(forecast["main"]["pressure"])
    timestamps.append(dt.datetime.fromtimestamp(forecast["dt"]))
import matplotlib.pyplot as plt
plt.bar(timestamps, pressures)
plt.xticks(rotation=45)
plt.ylim(960, 1050)
plt.grid()
plt.ylabel("Air pressure (hPa)")
plt.title(f"Forecasted air pressure in {CITY}")
plt.show()
../../_images/33abcc2cec5efd503caa3d8321141d712f311a424c8b88fcf1b1758fa88c2654.png

Exercise#

  • Make a new forecast request for your own hometown. Call your response something else than responseF.

  • If available, plot the humidity like we did with air pressure.

Precipitation#

  • … comes in two main flavours: rain and snow.

  • We need to check which is present and set to zero if it is abscent.

rain = []
snow = []
for forecast in responseF["list"]:
    try: # Check if rain is present in forecast
        rain.append(forecast["rain"]["3h"])
    except KeyError:
        rain.append(0)
    try: # Check if snow is present in forecast
        snow.append(forecast["snow"]["3h"])
    except KeyError:
        snow.append(0)
# Stacked bar chart with rain and snow
plt.bar(timestamps, rain, label="Rain")
plt.bar(timestamps, snow, label="Snow")
plt.xticks(rotation=45)
plt.grid()
plt.ylabel("Precipitation (mm)")
plt.title(f"Forecasted precipitation in {CITY}")
plt.legend()
plt.show()
../../_images/e380b615d2c3b1c0577a0feb1fb34cfa359b37b004f24fb56c8e85046e5bdbcb.png

Live coding#

  • Create a Streamlit app with these elements:

    • City selector (can we make use og the Geocoding API?)

    • Property selector

    • Print selected data