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': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'base': 'stations', 'main': {'temp': 282.47, 'feels_like': 282.47, 'temp_min': 282.47, 'temp_max': 283.89, 'pressure': 1016, 'humidity': 84, 'sea_level': 1016, 'grnd_level': 1000}, 'visibility': 10000, 'wind': {'speed': 0.73, 'deg': 164, 'gust': 0.72}, 'clouds': {'all': 3}, 'dt': 1760546319, 'sys': {'type': 2, 'id': 2006772, 'country': 'NO', 'sunrise': 1760507712, 'sunset': 1760544559}, 'timezone': 7200, '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': 1760551200, 'main': {'temp': 296.4, 'feels_like': 296.61, 'temp_min': 293.93, 'temp_max': 296.4, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 70, 'temp_kf': 2.47}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 75}, 'wind': {'speed': 5.07, 'deg': 294, 'gust': 6.15}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-15 18:00:00'}, {'dt': 1760562000, 'main': {'temp': 295.01, 'feels_like': 295.18, 'temp_min': 292.24, 'temp_max': 295.01, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 74, 'temp_kf': 2.77}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}], 'clouds': {'all': 83}, 'wind': {'speed': 1.03, 'deg': 174, 'gust': 1.37}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-15 21:00:00'}, {'dt': 1760572800, 'main': {'temp': 293.67, 'feels_like': 293.79, 'temp_min': 292.3, 'temp_max': 293.67, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 77, 'temp_kf': 1.37}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 92}, 'wind': {'speed': 0.82, 'deg': 85, 'gust': 1.43}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-16 00:00:00'}, {'dt': 1760583600, 'main': {'temp': 291.66, 'feels_like': 291.58, 'temp_min': 291.66, 'temp_max': 291.66, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 77, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 95}, 'wind': {'speed': 0.84, 'deg': 348, 'gust': 1.06}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-16 03:00:00'}, {'dt': 1760594400, 'main': {'temp': 291.57, 'feels_like': 291.48, 'temp_min': 291.57, 'temp_max': 291.57, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 77, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 88}, 'wind': {'speed': 1.13, 'deg': 140, 'gust': 0.77}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-16 06:00:00'}, {'dt': 1760605200, 'main': {'temp': 292.63, 'feels_like': 292.57, 'temp_min': 292.63, 'temp_max': 292.63, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 74, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 79}, 'wind': {'speed': 3.25, 'deg': 158, 'gust': 2.44}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-16 09:00:00'}, {'dt': 1760616000, 'main': {'temp': 295.26, 'feels_like': 295.12, 'temp_min': 295.26, 'temp_max': 295.26, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 997, 'humidity': 61, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 89}, 'wind': {'speed': 2.59, 'deg': 220, 'gust': 1.42}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-16 12:00:00'}, {'dt': 1760626800, 'main': {'temp': 294.73, 'feels_like': 294.64, 'temp_min': 294.73, 'temp_max': 294.73, 'pressure': 1011, 'sea_level': 1011, 'grnd_level': 995, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 89}, 'wind': {'speed': 4.65, 'deg': 268, 'gust': 4.73}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-16 15:00:00'}, {'dt': 1760637600, 'main': {'temp': 294.47, 'feels_like': 294.35, 'temp_min': 294.47, 'temp_max': 294.47, 'pressure': 1011, 'sea_level': 1011, 'grnd_level': 995, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 90}, 'wind': {'speed': 4.19, 'deg': 280, 'gust': 5.79}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-16 18:00:00'}, {'dt': 1760648400, 'main': {'temp': 292.47, 'feels_like': 292.52, 'temp_min': 292.47, 'temp_max': 292.47, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 996, 'humidity': 79, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 2.02, 'deg': 116, 'gust': 3.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-16 21:00:00'}, {'dt': 1760659200, 'main': {'temp': 291.36, 'feels_like': 291.61, 'temp_min': 291.36, 'temp_max': 291.36, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 996, 'humidity': 91, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 4.19, 'deg': 117, 'gust': 4.56}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-17 00:00:00'}, {'dt': 1760670000, 'main': {'temp': 290.9, 'feels_like': 291.08, 'temp_min': 290.9, 'temp_max': 290.9, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 90, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 1.53, 'deg': 96, 'gust': 1.07}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-17 03:00:00'}, {'dt': 1760680800, 'main': {'temp': 290.8, 'feels_like': 290.89, 'temp_min': 290.8, 'temp_max': 290.8, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 87, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 97}, 'wind': {'speed': 2.05, 'deg': 167, 'gust': 2.22}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-17 06:00:00'}, {'dt': 1760691600, 'main': {'temp': 292.06, 'feels_like': 292.1, 'temp_min': 292.06, 'temp_max': 292.06, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 996, 'humidity': 80, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 99}, 'wind': {'speed': 0.52, 'deg': 137, 'gust': 0.62}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-17 09:00:00'}, {'dt': 1760702400, 'main': {'temp': 294.29, 'feels_like': 294.31, 'temp_min': 294.29, 'temp_max': 294.29, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 71, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 86}, 'wind': {'speed': 4.53, 'deg': 249, 'gust': 3.09}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-17 12:00:00'}, {'dt': 1760713200, 'main': {'temp': 295.1, 'feels_like': 295.1, 'temp_min': 295.1, 'temp_max': 295.1, 'pressure': 1010, 'sea_level': 1010, 'grnd_level': 994, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 98}, 'wind': {'speed': 4.76, 'deg': 274, 'gust': 4.66}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-17 15:00:00'}, {'dt': 1760724000, 'main': {'temp': 294.48, 'feels_like': 294.47, 'temp_min': 294.48, 'temp_max': 294.48, 'pressure': 1010, 'sea_level': 1010, 'grnd_level': 994, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 99}, 'wind': {'speed': 2.36, 'deg': 286, 'gust': 4.35}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-17 18:00:00'}, {'dt': 1760734800, 'main': {'temp': 292.17, 'feels_like': 292.35, 'temp_min': 292.17, 'temp_max': 292.17, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 996, 'humidity': 85, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 3.32, 'deg': 121, 'gust': 3.42}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-17 21:00:00'}, {'dt': 1760745600, 'main': {'temp': 291.79, 'feels_like': 291.9, 'temp_min': 291.79, 'temp_max': 291.79, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 996, 'humidity': 84, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 100}, 'wind': {'speed': 4.41, 'deg': 110, 'gust': 4.99}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-18 00:00:00'}, {'dt': 1760756400, 'main': {'temp': 292.28, 'feels_like': 292.29, 'temp_min': 292.28, 'temp_max': 292.28, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 78, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 99}, 'wind': {'speed': 1.81, 'deg': 135, 'gust': 1.8}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-18 03:00:00'}, {'dt': 1760767200, 'main': {'temp': 290.92, 'feels_like': 291.02, 'temp_min': 290.92, 'temp_max': 290.92, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 996, 'humidity': 87, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'clouds': {'all': 98}, 'wind': {'speed': 1.66, 'deg': 305, 'gust': 1.66}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-18 06:00:00'}, {'dt': 1760778000, 'main': {'temp': 293, 'feels_like': 293.05, 'temp_min': 293, 'temp_max': 293, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 77, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 80}, 'wind': {'speed': 1.49, 'deg': 243, 'gust': 1.21}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-18 09:00:00'}, {'dt': 1760788800, 'main': {'temp': 294.5, 'feels_like': 294.57, 'temp_min': 294.5, 'temp_max': 294.5, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 72, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 46}, 'wind': {'speed': 3.43, 'deg': 250, 'gust': 2.72}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-18 12:00:00'}, {'dt': 1760799600, 'main': {'temp': 295.21, 'feels_like': 295.27, 'temp_min': 295.21, 'temp_max': 295.21, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 47}, 'wind': {'speed': 4.74, 'deg': 268, 'gust': 4.53}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-18 15:00:00'}, {'dt': 1760810400, 'main': {'temp': 294.86, 'feels_like': 294.86, 'temp_min': 294.86, 'temp_max': 294.86, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 68, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 65}, 'wind': {'speed': 1.88, 'deg': 246, 'gust': 2.77}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-18 18:00:00'}, {'dt': 1760821200, 'main': {'temp': 292.66, 'feels_like': 292.78, 'temp_min': 292.66, 'temp_max': 292.66, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 998, 'humidity': 81, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}], 'clouds': {'all': 76}, 'wind': {'speed': 3.03, 'deg': 116, 'gust': 3.37}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-18 21:00:00'}, {'dt': 1760832000, 'main': {'temp': 291.74, 'feels_like': 291.85, 'temp_min': 291.74, 'temp_max': 291.74, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 84, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 43}, 'wind': {'speed': 4.4, 'deg': 96, 'gust': 4.34}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-19 00:00:00'}, {'dt': 1760842800, 'main': {'temp': 291.48, 'feels_like': 291.43, 'temp_min': 291.48, 'temp_max': 291.48, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 79, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 32}, 'wind': {'speed': 2.17, 'deg': 88, 'gust': 2.12}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-19 03:00:00'}, {'dt': 1760853600, 'main': {'temp': 291, 'feels_like': 291.01, 'temp_min': 291, 'temp_max': 291, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 83, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 30}, 'wind': {'speed': 1.28, 'deg': 345, 'gust': 1.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-19 06:00:00'}, {'dt': 1760864400, 'main': {'temp': 292.99, 'feels_like': 292.96, 'temp_min': 292.99, 'temp_max': 292.99, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 74, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 6}, 'wind': {'speed': 2.19, 'deg': 255, 'gust': 1.75}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-19 09:00:00'}, {'dt': 1760875200, 'main': {'temp': 294.7, 'feels_like': 294.66, 'temp_min': 294.7, 'temp_max': 294.7, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 6}, 'wind': {'speed': 4.34, 'deg': 248, 'gust': 2.85}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-19 12:00:00'}, {'dt': 1760886000, 'main': {'temp': 297.25, 'feels_like': 297.2, 'temp_min': 297.25, 'temp_max': 297.25, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 996, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 6}, 'wind': {'speed': 5.17, 'deg': 280, 'gust': 5.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-19 15:00:00'}, {'dt': 1760896800, 'main': {'temp': 295.58, 'feels_like': 295.47, 'temp_min': 295.58, 'temp_max': 295.58, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 61, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 7}, 'wind': {'speed': 3.39, 'deg': 318, 'gust': 6.22}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-19 18:00:00'}, {'dt': 1760907600, 'main': {'temp': 292.56, 'feels_like': 292.59, 'temp_min': 292.56, 'temp_max': 292.56, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 78, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.07, 'deg': 112, 'gust': 2.9}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-19 21:00:00'}, {'dt': 1760918400, 'main': {'temp': 291.53, 'feels_like': 291.59, 'temp_min': 291.53, 'temp_max': 291.53, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 83, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 1}, 'wind': {'speed': 4.3, 'deg': 99, 'gust': 4.03}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-20 00:00:00'}, {'dt': 1760929200, 'main': {'temp': 291.28, 'feels_like': 291.19, 'temp_min': 291.28, 'temp_max': 291.28, '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': 5}, 'wind': {'speed': 0.92, 'deg': 72, 'gust': 1.81}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-20 03:00:00'}, {'dt': 1760940000, 'main': {'temp': 290.69, 'feels_like': 290.69, 'temp_min': 290.69, 'temp_max': 290.69, 'pressure': 1013, 'sea_level': 1013, 'grnd_level': 997, 'humidity': 84, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 7}, 'wind': {'speed': 1.11, 'deg': 262, 'gust': 1.43}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-10-20 06:00:00'}, {'dt': 1760950800, 'main': {'temp': 292.7, 'feels_like': 292.64, 'temp_min': 292.7, 'temp_max': 292.7, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 74, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 63}, 'wind': {'speed': 1.69, 'deg': 246, 'gust': 1.47}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-20 09:00:00'}, {'dt': 1760961600, 'main': {'temp': 294.16, 'feels_like': 294.12, 'temp_min': 294.16, 'temp_max': 294.16, 'pressure': 1014, 'sea_level': 1014, 'grnd_level': 997, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 35}, 'wind': {'speed': 3.76, 'deg': 245, 'gust': 2.44}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-20 12:00:00'}, {'dt': 1760972400, 'main': {'temp': 295.47, 'feels_like': 295.43, 'temp_min': 295.47, 'temp_max': 295.47, 'pressure': 1012, 'sea_level': 1012, 'grnd_level': 995, 'humidity': 64, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 4}, 'wind': {'speed': 3.88, 'deg': 256, 'gust': 3.37}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-10-20 15:00:00'}], 'city': {'id': 2561668, 'name': 'Agadir', 'coord': {'lat': 30.4202, 'lon': -9.5982}, 'country': 'MA', 'population': 698310, 'timezone': 3600, 'sunrise': 1760510425, 'sunset': 1760551652}}

# 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-10-15 18:00:00
2025-10-15 21:00:00
2025-10-16 00:00:00
2025-10-16 03:00:00
2025-10-16 06:00:00
2025-10-16 09:00:00
2025-10-16 12:00:00
2025-10-16 15:00:00
2025-10-16 18:00:00
2025-10-16 21:00:00
2025-10-17 00:00:00
2025-10-17 03:00:00
2025-10-17 06:00:00
2025-10-17 09:00:00
2025-10-17 12:00:00
2025-10-17 15:00:00
2025-10-17 18:00:00
2025-10-17 21:00:00
2025-10-18 00:00:00
2025-10-18 03:00:00
2025-10-18 06:00:00
2025-10-18 09:00:00
2025-10-18 12:00:00
2025-10-18 15:00:00
2025-10-18 18:00:00
2025-10-18 21:00:00
2025-10-19 00:00:00
2025-10-19 03:00:00
2025-10-19 06:00:00
2025-10-19 09:00:00
2025-10-19 12:00:00
2025-10-19 15:00:00
2025-10-19 18:00:00
2025-10-19 21:00:00
2025-10-20 00:00:00
2025-10-20 03:00:00
2025-10-20 06:00:00
2025-10-20 09:00:00
2025-10-20 12:00:00
2025-10-20 15: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/e7dc8b8b358159e0af1ba4e49faca3551771a92c19cf2ec0db865b171cecd17b.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/86353f2ebb2768e8269713af42ab26b37f90db08eb06d5305376119ed50273a7.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