Learning how to build a weather app in Python is the perfect first API project for a beginner. You will sign up for a free API key, send a real request over the internet, parse the JSON response, and display live weather data for any city in the world — all in under 60 lines of code. By the end, you will understand how APIs work by actually using one, not just reading about them.
This tutorial assumes you know basic Python (variables, functions, dictionaries). If you need a refresher, start with our guide to learning coding from scratch. For a quick primer on the concepts first, read what an API is. If you have never built one before, this how to build a weather app in python guide keeps every step explicit, so you will never feel lost.
Step 1: Get a Free OpenWeatherMap API Key
An API key is like a password that identifies your app to the weather service. OpenWeatherMap offers a generous free tier — perfect for learning.
- Go to openweathermap.org and create a free account.
- Open the API keys tab in your account dashboard.
- Copy your default key (or generate a new one). It looks like a long string of letters and numbers.
- Important: new keys can take 10–60 minutes to activate. If you get an error right away, wait and try again.
Keep your key private. Never commit it to a public GitHub repository — store it in a variable or an environment variable, as shown below.
Step 2: Install the Requests Library
Python’s requests library makes HTTP calls simple. Install it with pip:
pip install requests
The requests library is the standard way Python talks to web APIs. The official requests documentation is excellent if you want to explore timeouts, headers, and sessions later.
Step 3: Write the Core Script That Fetches Weather
Here is the complete working script. Save it as weather.py, replace YOUR_API_KEY with your real key, and run it with python weather.py.
import os
import requests
API_KEY = os.getenv("OWM_API_KEY", "YOUR_API_KEY")
BASE_URL = "https://api.openweathermap.org/data/2.5/weather"
def get_weather(city):
params = {
"q": city,
"appid": API_KEY,
"units": "metric", # use "imperial" for Fahrenheit
}
response = requests.get(BASE_URL, params=params, timeout=10)
response.raise_for_status() # raises an error for bad responses
return response.json()
def display(data):
name = data["name"]
temp = data["main"]["temp"]
feels = data["main"]["feels_like"]
humidity = data["main"]["humidity"]
desc = data["weather"][0]["description"]
wind = data["wind"]["speed"]
print(f"\nWeather in {name}:")
print(f" Temperature: {temp}°C (feels like {feels}°C)")
print(f" Condition: {desc.capitalize()}")
print(f" Humidity: {humidity}%")
print(f" Wind speed: {wind} m/s")
if __name__ == "__main__":
city = input("Enter a city name: ").strip()
data = get_weather(city)
display(data)
Step 4: Understand How the Code Works
Let us break down what happens when you run the script in this how to build a weather app in python walkthrough:
- Parameters: the
paramsdictionary becomes the query string of the URL —?q=London&appid=...&units=metric. Theqparameter is the city,appidis your key, andunits=metricasks for Celsius. - The request:
requests.get()sends an HTTP GET request and returns a response object. Thetimeout=10prevents your program from hanging forever if the network stalls. - Error detection:
raise_for_status()throws an exception for HTTP errors like 401 (bad API key) or 404 (city not found), so failures do not silently produce garbage output. - Parsing JSON:
response.json()converts the JSON text into nested Python dictionaries and lists. Temperature lives atdata["main"]["temp"], and the description atdata["weather"][0]["description"]becauseweatheris a list.
Step 5: Handle Errors Gracefully (Bad City, No Internet)
Real apps do not crash on bad input. Wrap the fetch in error handling so a typo or a dead connection produces a friendly message instead of a traceback.
import requests
def get_weather_safe(city, api_key):
try:
response = requests.get(
"https://api.openweathermap.org/data/2.5/weather",
params={"q": city, "appid": api_key, "units": "metric"},
timeout=10,
)
response.raise_for_status()
return response.json(), None
except requests.exceptions.ConnectionError:
return None, "No internet connection. Check your network and try again."
except requests.exceptions.Timeout:
return None, "The request timed out. Try again in a moment."
except requests.exceptions.HTTPError as e:
if response.status_code == 401:
return None, "Invalid API key. Check your OpenWeatherMap key."
if response.status_code == 404:
return None, f"City '{city}' not found. Check the spelling."
return None, f"Weather service error: {e}"
data, error = get_weather_safe("Londonn", "YOUR_API_KEY")
if error:
print("Error:", error)
else:
print("Temperature:", data["main"]["temp"])
Why this matters: catching specific exceptions (ConnectionError, Timeout, HTTPError) lets you give the user a useful message for each failure mode. This pattern applies to every API project you will ever build.
Step 6 (Bonus): Add a Simple GUI with Tkinter
Want a window instead of a terminal? Tkinter ships with Python, so there is nothing to install. This version reuses the same get_weather logic and adds a tiny interface.
import tkinter as tk
from tkinter import messagebox
import requests
API_KEY = "YOUR_API_KEY"
def show_weather():
city = entry.get().strip()
if not city:
return
try:
r = requests.get(
"https://api.openweathermap.org/data/2.5/weather",
params={"q": city, "appid": API_KEY, "units": "metric"},
timeout=10,
)
r.raise_for_status()
d = r.json()
temp = d["main"]["temp"]
desc = d["weather"][0]["description"]
result.set(f"{d['name']}: {temp}°C, {desc}")
except Exception as e:
messagebox.showerror("Error", f"Could not fetch weather:\n{e}")
root = tk.Tk()
root.title("Weather App")
root.geometry("320x160")
tk.Label(root, text="Enter city:").pack(pady=8)
entry = tk.Entry(root, width=30)
entry.pack()
tk.Button(root, text="Get Weather", command=show_weather).pack(pady=8)
result = tk.StringVar()
tk.Label(root, textvariable=result, font=("Arial", 12)).pack(pady=8)
root.mainloop()
How it works: tk.Entry captures the city name, the button triggers show_weather, and a StringVar label displays the result. mainloop() keeps the window open and responsive.
Step 7 (Bonus): Outline of a Flask Web Version
For a browser-based version, Flask turns your script into a web app. The structure:
pip install flask, then createapp.pywith two routes:/shows a form,/weatherreads the city from the form, calls the API, and renders the result.- Store the weather-fetching function from Step 3 in a separate
weather_service.pymodule and import it — keeping API logic separate from web logic. - Use Jinja templates (
templates/index.html,templates/result.html) instead of returning raw strings, so the page looks good. - Read the API key from an environment variable with
os.getenv("OWM_API_KEY")— never hardcode it in a web app you deploy.
This is a natural next step after the GUI version, and it counts as one of our easy Python projects for beginners taken to the next level.
Common Problems and Fixes
- 401 Unauthorized: your key is wrong or not activated yet. Wait up to an hour after creating it.
- 404 city not found: check spelling, or add a country code like
q=Paris,FR. - SSL or connection errors: check your internet connection and firewall; corporate networks sometimes block API traffic.
- KeyError on a field: print the raw JSON with
print(data)to see the actual structure — API responses change occasionally.
How to Build a Weather App in Python: What You Learned
You now know how to build a weather app in Python from scratch: obtaining an API key, making HTTP requests with requests, parsing JSON, handling real-world errors, and even wrapping it in a GUI. These are the exact skills behind nearly every API integration — stock trackers, news apps, chatbots, and dashboards all follow the same fetch-parse-display pattern.
Bonus: Add a 5-Day Forecast
OpenWeatherMap’s forecast endpoint returns predictions in 3-hour steps. This script grabs the forecast and prints one summary line per day — a great extension of the core skills you just learned.
import requests
from collections import defaultdict
API_KEY = "YOUR_API_KEY"
def get_forecast(city):
r = requests.get(
"https://api.openweathermap.org/data/2.5/forecast",
params={"q": city, "appid": API_KEY, "units": "metric"},
timeout=10,
)
r.raise_for_status()
return r.json()
def daily_summary(city):
data = get_forecast(city)
by_day = defaultdict(list)
for entry in data["list"]:
day = entry["dt_txt"][:10] # "2026-10-12 09:00:00" -> "2026-10-12"
by_day[day].append(entry["main"]["temp"])
print(f"\n5-day forecast for {data['city']['name']}:")
for day in sorted(by_day)[:5]:
temps = by_day[day]
print(f" {day}: low {min(temps):.1f}°C, high {max(temps):.1f}°C")
daily_summary("Tokyo")
How it works: the forecast endpoint returns a list of 3-hour predictions. The script groups temperatures by date using a defaultdict, then prints the min and max per day. Notice how the same fetch-and-parse pattern from the current-weather script applies directly to a new endpoint — that is the power of learning the pattern, not just the code.
Frequently Asked Questions
Is the OpenWeatherMap free tier really free? Yes. The free plan includes current weather and 5-day forecast with generous call limits — far more than a learning project needs. You only pay if you upgrade for commercial use.
Why do I get a 401 error with a brand-new key? New API keys take 10–60 minutes to activate on OpenWeatherMap’s servers. Wait a bit and retry before assuming the key is wrong.
Can I build this without an API key? Not with OpenWeatherMap, but the skills transfer. Some APIs need no key at all for basic use, and the request/parse/display pattern is identical everywhere.
How do I keep my API key secret? Set it as an environment variable (export OWM_API_KEY="your_key" on Mac/Linux, or System Properties → Environment Variables on Windows) and read it with os.getenv(), as the main script does. Never paste keys into public code or screenshots.
What’s Next
Deepen your API skills by reading what an API is to understand REST, endpoints, and authentication, try more Python projects for beginners with source code to practice, and review how to learn coding from scratch to plan your next milestone.