Skip to Content
Course content

420: Building a Weather CLI Tool Using a Public API

Click on the "Edit" button in the top corner of the screen to edit your slide content.

One thing I see a lot of developers stumble over when they first touch an API is the belief that an API call is just a fancy function call. They think that when they request weather data, the server sends back a clean Python object with attributes like weather.temperature or weather.city. It feels intuitive, but it's completely wrong.

In reality, an API doesn't know Python exists. It sends back a long string of text formatted as JSON (JavaScript Object Notation). If you try to treat that response like a Python object immediately, your program will crash with an AttributeError or a TypeError. You aren't receiving a "Weather Object"; you're receiving a serialized string that you have to decode into a dictionary before you can even think about accessing the temperature.

The "Magic Object" Fallacy vs. Parsing JSON Dictionaries

Let's look at how this actually plays out. If you use the requests library to hit a weather endpoint, the raw response is an HTTP object. To get the data, you have to call the .json() method, which transforms that string into a nested Python dictionary. I've spent way too many hours debugging "NoneType" errors simply because I forgot that API data is often nested three or four levels deep.

import requests

# This is a simplified version of what happens
API_KEY = "your_api_key_here"
city = "London"
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units=metric"

response = requests.get(url)
data = response.json()

# WRONG: Thinking it's a flat object
# print(data.temperature) 

# RIGHT: Navigating the dictionary hierarchy
temp = data['main']['temp']
description = data['weather'][0]['description']

print(f"The weather in {city} is {temp}°C with {description}.")

Notice that data['weather'] is actually a list containing a dictionary. That's a classic API quirk. You can't just ask for the description; you have to ask for the first item in the weather list, and then ask for the description key within that item.

Hardcoding Keys is "Fine for Now" vs. Environment Variables

When you're building a CLI tool, it's tempting to just paste your API key directly into the script. I did this early in my career, pushed it to GitHub, and had my account suspended within ten minutes because bots scrape public repos for keys. Never do this. Even for a small project, get in the habit of using environment variables.

By using the os module, you keep your secrets out of your code. It makes your tool portable and secure. If you share your code with a teammate, they can use their own key without you having to edit the source file for them.

import os
from dotenv import load_dotenv # You'll need to pip install python-dotenv

load_dotenv()
API_KEY = os.getenv("OPENWEATHER_API_KEY")

if not API_KEY:
    raise ValueError("Missing API Key! Please set OPENWEATHER_API_KEY in your .env file.")

Assuming the Internet Always Works vs. Defensive Request Handling

Your code might work perfectly on your machine today, but the moment a user has a spotty connection or the API server goes down for maintenance, your CLI tool will throw a massive, ugly traceback. A professional tool handles failure gracefully. You don't want your user seeing a ConnectionError; you want them to see "Sorry, I couldn't reach the weather service right now."

I always recommend wrapping your request in a try-except block and checking the status code. A 200 OK is great, but a 404 Not Found (usually meaning the user typed the city name wrong) needs a specific message.

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status() # This raises an exception for 4xx or 5xx errors
    data = response.json()
except requests.exceptions.HTTPError:
    print("City not found. Please check your spelling.")
    exit(1)
except requests.exceptions.ConnectionError:
    print("Network error. Check your internet connection.")
    exit(1)
except requests.exceptions.Timeout:
    print("The server took too long to respond.")
    exit(1)



📋 Practical Task

Build a "City-to-City" Weather Comparison Tool

Your task is to create a CLI tool that accepts two city names as command-line arguments (using sys.argv) and prints a comparison of their current temperatures.

Requirements:

  • The script should be executed as python weather_compare.py London Tokyo.
  • Use an environment variable for your API key (do not hardcode it).
  • Implement a try-except block to handle cases where one or both cities are not found by the API.
  • The output should clearly state which city is warmer and by how many degrees (e.g., "Tokyo is 5.2°C warmer than London").
  • Ensure you are parsing the nested JSON response correctly to extract the temperature from the 'main' key.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.