Python Central HubSign in

Python Projects / Intermediate

Simple Weather Forecast App

Build a Tkinter weather app in Python using the OpenWeatherMap API — fetch current conditions for any city, then add a 5-day forecast, weather icons, unit toggles, geolocation, caching, and graceful error handling.

Abstract

Consuming a real web API is a rite of passage, and weather is the perfect first API: free, well-documented, and instantly gratifying. In this tutorial you build a Tkinter app that queries the OpenWeatherMap API for a city's current conditions and displays temperature, "feels like", humidity, and a description. Then you grow it into something polished — a 5-day forecast, weather icons, °C/°F toggle, geolocation, response caching, and the kind of error handling that separates a demo from a tool.

You will leave understanding:

  • How to register for and use an API key safely.
  • The request → JSON → parse → display pipeline behind every API client.
  • How to read nested JSON (data["weather"][0]["description"]) without crashing.
  • Why you check status_code and handle the unhappy paths first.

Prerequisites

  • Python 3.6 or above.
  • A text editor or IDE.
  • The requests library: pip install requests.
  • A free OpenWeatherMap API key from openweathermap.org/api.
  • Tkinter (bundled with Python).

Getting Started

Get an API key

  1. Sign up at openweathermap.org (free tier is plenty).
  2. Copy your API key from the dashboard.
  3. New keys can take a few minutes to activate — don't panic if the first call 401s.

Create the project

  1. Create a folder named weather-app.
  2. Inside it, create simple_weather_forecast_app.py.
  3. Install the dependency: pip install requests.

Write the code

simple_weather_forecast_app.pySource
simple_weather_forecast_app.py
"""
Simple Weather Forecast App

A Python application that fetches and displays weather forecast data. Features include:
- Fetching weather data from an API.
- Displaying the forecast in a user-friendly format.
"""

import requests
from tkinter import Tk, Label, Entry, Button, messagebox

API_KEY = "your_openweathermap_api_key"
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"


class WeatherForecastApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Simple Weather Forecast App")

        Label(root, text="Enter City Name:").grid(row=0, column=0, padx=10, pady=10)
        self.city_entry = Entry(root, width=30)
        self.city_entry.grid(row=0, column=1, padx=10, pady=10)

        Button(root, text="Get Weather", command=self.get_weather).grid(row=1, column=0, columnspan=2, pady=10)

        self.result_label = Label(root, text="", wraplength=400, justify="left")
        self.result_label.grid(row=2, column=0, columnspan=2, padx=10, pady=10)

    def get_weather(self):
        """Fetch weather data for the entered city."""
        city = self.city_entry.get()
        if not city:
            messagebox.showerror("Error", "Please enter a city name.")
            return

        params = {"q": city, "appid": API_KEY, "units": "metric"}
        try:
            response = requests.get(BASE_URL, params=params)
            data = response.json()

            if response.status_code == 200:
                weather = data["weather"][0]["description"].capitalize()
                temp = data["main"]["temp"]
                feels_like = data["main"]["feels_like"]
                humidity = data["main"]["humidity"]

                result = (
                    f"Weather in {city}:\n"
                    f"Condition: {weather}\n"
                    f"Temperature: {temp}°C\n"
                    f"Feels Like: {feels_like}°C\n"
                    f"Humidity: {humidity}%"
                )
                self.result_label.config(text=result)
            else:
                messagebox.showerror("Error", data.get("message", "Failed to fetch weather data."))
        except Exception as e:
            messagebox.showerror("Error", f"An error occurred: {e}")


def main():
    root = Tk()
    app = WeatherForecastApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()

Run it

cmd
C:\Users\Your Name\weather-app> python simple_weather_forecast_app.py
# Enter a city name, click "Get Weather".
# Make sure you replaced API_KEY with your real key first.

How it fits together

Read from the top: this is what runs when you execute the file, and which function calls which. It is generated from the code, so it cannot drift from it.

diagram Diagram mermaid

Step-by-Step Explanation

1. The endpoint and key

Python
API_KEY = "your_openweathermap_api_key"
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"

BASE_URL is the "current weather" endpoint. The key authenticates you and counts against your rate limit.

2. Building the request

Python
params = {"q": city, "appid": API_KEY, "units": "metric"}
response = requests.get(BASE_URL, params=params)
data = response.json()

Passing a params dict lets requests build the query string for you (?q=London&appid=...&units=metric) and handles URL-encoding spaces and special characters. units="metric" gives Celsius; "imperial" gives Fahrenheit.

3. Check status before parsing

Python
if response.status_code == 200:
    weather = data["weather"][0]["description"].capitalize()
    temp = data["main"]["temp"]
    ...
else:
    messagebox.showerror("Error", data.get("message", "Failed to fetch weather data."))

This is the most important habit in API work: handle the failure path first. A typo'd city returns 404 with a helpful message field — surface it instead of crashing on a missing key.

4. Reading nested JSON

Python
data["weather"][0]["description"]   # list → first item → field
data["main"]["temp"]                # nested dict

OpenWeatherMap nests weather inside a list (a place can have several conditions) and groups numbers under main. Knowing the shape of the JSON is half the battle — print data once and study it.

Protect Your API Key

Never hard-code secrets. Use an environment variable:

Python
import os
API_KEY = os.environ["OWM_API_KEY"]   # set OWM_API_KEY in your shell, not in code
cmd
# PowerShell
$env:OWM_API_KEY = "your_key_here"

This keeps the key out of screenshots, git history, and shared code.

Add a 5-Day Forecast

The forecast endpoint returns data in 3-hour steps:

Python
FORECAST_URL = "http://api.openweathermap.org/data/2.5/forecast"
 
def five_day(city):
    r = requests.get(FORECAST_URL,
                     params={"q": city, "appid": API_KEY, "units": "metric"},
                     timeout=10)
    r.raise_for_status()
    # Pick the midday (12:00) reading for each day
    return [item for item in r.json()["list"] if "12:00:00" in item["dt_txt"]]

Loop the result into five labels or a small table.

Show Weather Icons

Each condition carries an icon code:

Python
from tkinter import PhotoImage
import requests, io
 
icon_code = data["weather"][0]["icon"]                       # e.g. "10d"
url = f"http://openweathermap.org/img/wn/{icon_code}@2x.png"
img_bytes = requests.get(url, timeout=10).content
# Save then load, or use Pillow's ImageTk for in-memory display

Add a °C / °F Toggle

Store the raw value once, convert for display:

Python
def to_fahrenheit(celsius):
    return celsius * 9 / 5 + 32
 
label.config(text=f"{temp}°C" if self.metric else f"{to_fahrenheit(temp):.1f}°F")

Common Mistakes

ProblemCauseFix
401 UnauthorizedKey missing, wrong, or not yet activeCheck the key; wait a few minutes after signup
KeyError: 'main'Parsed JSON before checking statusGuard with if status_code == 200 first
App freezes on slow networkNo timeoutAdd timeout=10; fetch on a background thread
Spaces in city name break URLManual string concatenationUse the params= dict — it encodes for you
Wrong temperature scaleForgot unitsSet units="metric" or "imperial"
Rate-limit errorsPolling too oftenCache responses for a few minutes

Variations to Try

  1. Geolocation — detect the user's city via their IP and load it on launch.
  2. Hourly chart — plot the next 24 hours with matplotlib.
  3. Favorites — save a list of cities and refresh them all at once.
  4. Severe-weather alerts — use the One Call API's alerts field.
  5. Background by condition — change the window color for sunny/rainy/snow.
  6. Caching layer — store responses with a timestamp; skip the API within 10 minutes.
  7. Voice output — read the forecast aloud (see Weather App with Voice Commands).

Real-World Applications

  • Travel apps — destination weather at a glance.
  • Agriculture & logistics — planning around conditions.
  • Dashboards — a weather widget on a home-automation panel.
  • Event planning — outdoor-event go/no-go decisions.

Educational Value

  • REST APIs — keys, query parameters, JSON responses.
  • Defensive parsing — status checks and .get() with defaults.
  • Secret management — environment variables over hard-coding.
  • Caching & rate limits — being a good API citizen.

Next Steps

  • Move the key into an environment variable.
  • Add a 5-day forecast and weather icons.
  • Implement a °C/°F toggle and response caching.
  • Detect the user's city with geolocation.

Checkpoint exercise

This checkpoint practices the forecast summary: parse an inline JSON response, find each day's high and convert it to Fahrenheit.

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Conclusion

You built a weather app that turns a city name into live conditions, then layered on a forecast, icons, unit toggles, and caching — all on top of the same request → parse → display loop you'll reuse for every API you ever touch. The skill transfers directly: swap the endpoint and you have a stock ticker, a currency converter, or a news reader. Full source on GitHub. Explore more API projects on Python Central Hub.

Was this page helpful?

Let us know how we did