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': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'base': 'stations', 'main': {'temp': 271.92, 'feels_like': 271.92, 'temp_min': 271.92, 'temp_max': 272.77, 'pressure': 1001, 'humidity': 99, 'sea_level': 1001, 'grnd_level': 985}, 'visibility': 10000, 'wind': {'speed': 0.88, 'deg': 50, 'gust': 1.06}, 'clouds': {'all': 100}, 'dt': 1763971676, 'sys': {'type': 2, 'id': 2006772, 'country': 'NO', 'sunrise': 1763969672, 'sunset': 1763994753}, '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': 1763974800, 'main': {'temp': 285.4, 'feels_like': 283.78, 'temp_min': 285.4, 'temp_max': 293.92, 'pressure': 1024, 'sea_level': 1024, 'grnd_level': 1007, 'humidity': 42, 'temp_kf': -8.52}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.98, 'deg': 280, 'gust': 0.96}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 09:00:00'}, {'dt': 1763985600, 'main': {'temp': 288.83, 'feels_like': 287.5, 'temp_min': 288.83, 'temp_max': 295.68, 'pressure': 1024, 'sea_level': 1024, 'grnd_level': 1007, 'humidity': 40, 'temp_kf': -6.85}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.99, 'deg': 246, 'gust': 3.17}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 12:00:00'}, {'dt': 1763996400, 'main': {'temp': 292.79, 'feels_like': 291.78, 'temp_min': 292.79, 'temp_max': 296.49, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 37, 'temp_kf': -3.7}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.83, 'deg': 293, 'gust': 5.39}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-24 15:00:00'}, {'dt': 1764007200, 'main': {'temp': 294.08, 'feels_like': 293.48, 'temp_min': 294.08, 'temp_max': 294.08, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1004, 'humidity': 48, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4.91, 'deg': 308, 'gust': 5.97}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-24 18:00:00'}, {'dt': 1764018000, 'main': {'temp': 293.59, 'feels_like': 292.89, 'temp_min': 293.59, 'temp_max': 293.59, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 46, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.92, 'deg': 336, 'gust': 5.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-24 21:00:00'}, {'dt': 1764028800, 'main': {'temp': 292.46, 'feels_like': 291.78, 'temp_min': 292.46, 'temp_max': 292.46, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1004, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.62, 'deg': 43, 'gust': 2.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 00:00:00'}, {'dt': 1764039600, 'main': {'temp': 291.31, 'feels_like': 290.67, 'temp_min': 291.31, 'temp_max': 291.31, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.65, 'deg': 106, 'gust': 1.99}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 03:00:00'}, {'dt': 1764050400, 'main': {'temp': 289.88, 'feels_like': 289.31, 'temp_min': 289.88, 'temp_max': 289.88, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.71, 'deg': 113, 'gust': 1.37}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 06:00:00'}, {'dt': 1764061200, 'main': {'temp': 291.01, 'feels_like': 290.5, 'temp_min': 291.01, 'temp_max': 291.01, 'pressure': 1021, 'sea_level': 1021, 'grnd_level': 1004, 'humidity': 63, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03d'}], 'clouds': {'all': 38}, 'wind': {'speed': 0.99, 'deg': 177, 'gust': 0.94}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 09:00:00'}, {'dt': 1764072000, 'main': {'temp': 294.29, 'feels_like': 293.66, 'temp_min': 294.29, 'temp_max': 294.29, 'pressure': 1020, 'sea_level': 1020, 'grnd_level': 1003, 'humidity': 46, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}], 'clouds': {'all': 51}, 'wind': {'speed': 2.04, 'deg': 247, 'gust': 1.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 12:00:00'}, {'dt': 1764082800, 'main': {'temp': 294.69, 'feels_like': 294.02, 'temp_min': 294.69, 'temp_max': 294.69, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 43, 'temp_kf': 0}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': {'all': 100}, 'wind': {'speed': 1.5, 'deg': 276, 'gust': 2.84}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-25 15:00:00'}, {'dt': 1764093600, 'main': {'temp': 291.57, 'feels_like': 291.09, 'temp_min': 291.57, 'temp_max': 291.57, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 62, 'temp_kf': 0}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}], 'clouds': {'all': 65}, 'wind': {'speed': 1.35, 'deg': 120, 'gust': 1.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 18:00:00'}, {'dt': 1764104400, 'main': {'temp': 289.57, 'feels_like': 289.43, 'temp_min': 289.57, 'temp_max': 289.57, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 83, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 37}, 'wind': {'speed': 4.84, 'deg': 117, 'gust': 4.76}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-25 21:00:00'}, {'dt': 1764115200, 'main': {'temp': 288.57, 'feels_like': 288.49, 'temp_min': 288.57, 'temp_max': 288.57, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 89, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02n'}], 'clouds': {'all': 21}, 'wind': {'speed': 5.34, 'deg': 108, 'gust': 6.28}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 00:00:00'}, {'dt': 1764126000, 'main': {'temp': 288.77, 'feels_like': 288.35, 'temp_min': 288.77, 'temp_max': 288.77, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 75, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 2.61, 'deg': 79, 'gust': 3.05}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 03:00:00'}, {'dt': 1764136800, 'main': {'temp': 288.42, 'feels_like': 287.91, 'temp_min': 288.42, 'temp_max': 288.42, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 73, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 0.96, 'deg': 352, 'gust': 2}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 06:00:00'}, {'dt': 1764147600, 'main': {'temp': 289.48, 'feels_like': 288.97, 'temp_min': 289.48, 'temp_max': 289.48, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 69, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 4}, 'wind': {'speed': 1.41, 'deg': 176, 'gust': 0.42}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 09:00:00'}, {'dt': 1764158400, 'main': {'temp': 291.03, 'feels_like': 290.57, 'temp_min': 291.03, 'temp_max': 291.03, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 65, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 3}, 'wind': {'speed': 1.62, 'deg': 210, 'gust': 1.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 12:00:00'}, {'dt': 1764169200, 'main': {'temp': 291.59, 'feels_like': 291.03, 'temp_min': 291.59, 'temp_max': 291.59, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 59, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.23, 'deg': 256, 'gust': 2.53}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-26 15:00:00'}, {'dt': 1764180000, 'main': {'temp': 290.37, 'feels_like': 289.92, 'temp_min': 290.37, 'temp_max': 290.37, 'pressure': 1015, 'sea_level': 1015, 'grnd_level': 998, 'humidity': 68, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 4, 'deg': 158, 'gust': 3.98}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 18:00:00'}, {'dt': 1764190800, 'main': {'temp': 289.98, 'feels_like': 289.55, 'temp_min': 289.98, 'temp_max': 289.98, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 3}, 'wind': {'speed': 4.61, 'deg': 123, 'gust': 5.2}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-26 21:00:00'}, {'dt': 1764201600, 'main': {'temp': 289.93, 'feels_like': 289.41, 'temp_min': 289.93, 'temp_max': 289.93, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 6}, 'wind': {'speed': 4.13, 'deg': 106, 'gust': 4.45}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 00:00:00'}, {'dt': 1764212400, 'main': {'temp': 289.32, 'feels_like': 288.74, 'temp_min': 289.32, 'temp_max': 289.32, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 49}, 'wind': {'speed': 0.23, 'deg': 268, 'gust': 0.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 03:00:00'}, {'dt': 1764223200, 'main': {'temp': 288.87, 'feels_like': 288.33, 'temp_min': 288.87, 'temp_max': 288.87, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 70, 'temp_kf': 0}, 'weather': [{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}], 'clouds': {'all': 43}, 'wind': {'speed': 1.43, 'deg': 294, 'gust': 1.43}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 06:00:00'}, {'dt': 1764234000, 'main': {'temp': 291.03, 'feels_like': 290.52, 'temp_min': 291.03, 'temp_max': 291.03, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 63, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02d'}], 'clouds': {'all': 18}, 'wind': {'speed': 1.22, 'deg': 303, 'gust': 1.68}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 09:00:00'}, {'dt': 1764244800, 'main': {'temp': 292.46, 'feels_like': 291.96, 'temp_min': 292.46, 'temp_max': 292.46, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02d'}], 'clouds': {'all': 11}, 'wind': {'speed': 2.09, 'deg': 248, 'gust': 1.88}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 12:00:00'}, {'dt': 1764255600, 'main': {'temp': 293.03, 'feels_like': 292.56, 'temp_min': 293.03, 'temp_max': 293.03, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 999, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 4}, 'wind': {'speed': 4.26, 'deg': 277, 'gust': 3.67}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-27 15:00:00'}, {'dt': 1764266400, 'main': {'temp': 292.08, 'feels_like': 291.54, 'temp_min': 292.08, 'temp_max': 292.08, 'pressure': 1016, 'sea_level': 1016, 'grnd_level': 1000, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.52, 'deg': 288, 'gust': 3.78}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 18:00:00'}, {'dt': 1764277200, 'main': {'temp': 290.94, 'feels_like': 290.52, 'temp_min': 290.94, 'temp_max': 290.94, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 67, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.38, 'deg': 158, 'gust': 2.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-27 21:00:00'}, {'dt': 1764288000, 'main': {'temp': 289.9, 'feels_like': 289.56, 'temp_min': 289.9, 'temp_max': 289.9, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 74, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.03, 'deg': 121, 'gust': 3.58}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 00:00:00'}, {'dt': 1764298800, 'main': {'temp': 290, 'feels_like': 289.41, 'temp_min': 290, 'temp_max': 290, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 64, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.8, 'deg': 91, 'gust': 4.1}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 03:00:00'}, {'dt': 1764309600, 'main': {'temp': 289.87, 'feels_like': 289.11, 'temp_min': 289.87, 'temp_max': 289.87, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.7, 'deg': 44, 'gust': 2.91}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 06:00:00'}, {'dt': 1764320400, 'main': {'temp': 291.58, 'feels_like': 290.89, 'temp_min': 291.58, 'temp_max': 291.58, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1002, 'humidity': 54, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 1.09, 'deg': 345, 'gust': 1.88}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 09:00:00'}, {'dt': 1764331200, 'main': {'temp': 293.32, 'feels_like': 292.72, 'temp_min': 293.32, 'temp_max': 293.32, 'pressure': 1019, 'sea_level': 1019, 'grnd_level': 1002, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.33, 'deg': 270, 'gust': 1.95}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 12:00:00'}, {'dt': 1764342000, 'main': {'temp': 293.22, 'feels_like': 292.61, 'temp_min': 293.22, 'temp_max': 293.22, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 51, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.69, 'deg': 288, 'gust': 3.65}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'd'}, 'dt_txt': '2025-11-28 15:00:00'}, {'dt': 1764352800, 'main': {'temp': 292.82, 'feels_like': 292.2, 'temp_min': 292.82, 'temp_max': 292.82, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1000, 'humidity': 52, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.82, 'deg': 314, 'gust': 4.13}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 18:00:00'}, {'dt': 1764363600, 'main': {'temp': 291.71, 'feels_like': 291.14, 'temp_min': 291.71, 'temp_max': 291.71, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 58, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.84, 'deg': 115, 'gust': 2.36}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-28 21:00:00'}, {'dt': 1764374400, 'main': {'temp': 290.51, 'feels_like': 290.02, 'temp_min': 290.51, 'temp_max': 290.51, 'pressure': 1018, 'sea_level': 1018, 'grnd_level': 1001, 'humidity': 66, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 3.54, 'deg': 114, 'gust': 3.39}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 00:00:00'}, {'dt': 1764385200, 'main': {'temp': 290.02, 'feels_like': 289.25, 'temp_min': 290.02, 'temp_max': 290.02, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 57, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 2.86, 'deg': 94, 'gust': 2.82}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 03:00:00'}, {'dt': 1764396000, 'main': {'temp': 289.22, 'feels_like': 288.42, 'temp_min': 289.22, 'temp_max': 289.22, 'pressure': 1017, 'sea_level': 1017, 'grnd_level': 1001, 'humidity': 59, 'temp_kf': 0}, 'weather': [{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}], 'clouds': {'all': 0}, 'wind': {'speed': 0.64, 'deg': 4, 'gust': 1.79}, 'visibility': 10000, 'pop': 0, 'sys': {'pod': 'n'}, 'dt_txt': '2025-11-29 06:00:00'}], 'city': {'id': 2561668, 'name': 'Agadir', 'coord': {'lat': 30.4202, 'lon': -9.5982}, 'country': 'MA', 'population': 698310, 'timezone': 3600, 'sunrise': 1763968323, 'sunset': 1764005913}}
# 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-24 09:00:00
2025-11-24 12:00:00
2025-11-24 15:00:00
2025-11-24 18:00:00
2025-11-24 21:00:00
2025-11-25 00:00:00
2025-11-25 03:00:00
2025-11-25 06:00:00
2025-11-25 09:00:00
2025-11-25 12:00:00
2025-11-25 15:00:00
2025-11-25 18:00:00
2025-11-25 21:00:00
2025-11-26 00:00:00
2025-11-26 03:00:00
2025-11-26 06:00:00
2025-11-26 09:00:00
2025-11-26 12:00:00
2025-11-26 15:00:00
2025-11-26 18:00:00
2025-11-26 21:00:00
2025-11-27 00:00:00
2025-11-27 03:00:00
2025-11-27 06:00:00
2025-11-27 09:00:00
2025-11-27 12:00:00
2025-11-27 15:00:00
2025-11-27 18:00:00
2025-11-27 21:00:00
2025-11-28 00:00:00
2025-11-28 03:00:00
2025-11-28 06:00:00
2025-11-28 09:00:00
2025-11-28 12:00:00
2025-11-28 15:00:00
2025-11-28 18:00:00
2025-11-28 21:00:00
2025-11-29 00:00:00
2025-11-29 03:00:00
2025-11-29 06: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/494b9a67c378cf248dcd1012b9391d73b7b46115ebb4bdf29369825562de44fa.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/2a738601617d6296f0bc01865abf4818df7d4c607c58ec9a91b670c9fe567c90.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