Fetching live weather data is a practical real-world task, and building a Weather App helps you learn API integration, JSON handling, error management, and optionally web development. In this project, we will build:

Prerequisites

Step 1. Get an API Key from OpenWeatherMap

  1. Register (free) at openweathermap.org
  2. Navigate to the API section and generate an API key (called appid)
  3. Note: Free tier allows reasonable usage for learning; be mindful of rate limits

Store your API key securely (environment variable recommended).

Example for Linux/macOS:

export OWM_API_KEY="your_api_key_here"

Example for Windows (PowerShell):

$env:OWM_API_KEY="your_api_key_here"

Step 2. CLI Weather App (weather_cli.py)

import os
import requests
from datetime import datetime

API_KEY = os.getenv("OWM_API_KEY")
BASE_URL = "https://api.openweathermap.org/data/2.5/weather"

def get_weather(city: str, units: str = "metric"):
    if not API_KEY:
        raise RuntimeError("OpenWeatherMap API key not found. Set OWM_API_KEY environment variable.")

    params = {
        "q": city,
        "appid": API_KEY,
    }
    if units in ("metric", "imperial"):
        params["units"] = units

    try:
        response = requests.get(BASE_URL, params=params, timeout=10)
        response.raise_for_status()
        data = response.json()
    except requests.RequestException as e:
        print(f"Network or request error: {e}")
        return None

    if data.get("cod") != 200:
        print(f"Error from API: {data.get('message', 'Unknown error')}")
        return None

    return data

def format_and_print(data, units="metric"):
    name = data["name"]
    country = data["sys"]["country"]
    weather_desc = data["weather"][0]["description"].title()
    temp = data["main"]["temp"]
    feels_like = data["main"]["feels_like"]
    humidity = data["main"]["humidity"]
    wind_speed = data["wind"]["speed"]
    timestamp = datetime.fromtimestamp(data["dt"]).strftime("%Y-%m-%d %H:%M:%S")

    unit_symbol = "°C" if units == "metric" else "°F" if units == "imperial" else "K"
    speed_unit = "m/s" if units in ("metric", "") else "mph"

    print(f"\nWeather in {name}, {country} at {timestamp}")
    print("-" * 40)
    print(f"Condition     : {weather_desc}")
    print(f"Temperature   : {temp}{unit_symbol}")
    print(f"Feels Like    : {feels_like}{unit_symbol}")
    print(f"Humidity      : {humidity}%")
    print(f"Wind Speed    : {wind_speed} {speed_unit}")
    print("-" * 40)

def main():
    print("=== Python Weather CLI ===")
    city = input("Enter city name: ").strip()
    if not city:
        print("City name cannot be empty.")
        return

    unit_choice = input("Units - (1) Metric (°C), (2) Imperial (°F), (3) Kelvin: ").strip()
    if unit_choice == "1":
        units = "metric"
    elif unit_choice == "2":
        units = "imperial"
    else:
        units = None

    data = get_weather(city, units=units if units else "")
    if data:
        format_and_print(data, units=units if units else "")

if __name__ == "__main__":
    main()

Run with

python weather_cli.py

Step 3. Optional Web Version Using Flask (weather_web.py)

import os
from flask import Flask, request, render_template_string
import requests
from datetime import datetime

app = Flask(__name__)
API_KEY = os.getenv("OWM_API_KEY")
BASE_URL = "https://api.openweathermap.org/data/2.5/weather"

TEMPLATE = """
<!doctype html>
<title>Weather App</title>
<h2>Weather in {{ name }}, {{ country }}</h2>
<p><strong>Condition:</strong> {{ desc }}</p>
<p><strong>Temperature:</strong> {{ temp }} {{ unit }}</p>
<p><strong>Feels Like:</strong> {{ feels_like }} {{ unit }}</p>
<p><strong>Humidity:</strong> {{ humidity }}%</p>
<p><strong>Wind Speed:</strong> {{ wind }} {{ speed_unit }}</p>
<p><em>As of {{ timestamp }}</em></p>
<form method="get">
  <input name="city" placeholder="City name" required value="{{ name }}">
  <select name="units">
    <option value="metric" {% if units=='metric' %}selected{% endif %}>Metric (°C)</option>
    <option value="imperial" {% if units=='imperial' %}selected{% endif %}>Imperial (°F)</option>
    <option value="" {% if units=='' %}selected{% endif %}>Kelvin</option>
  </select>
  <button type="submit">Get Weather</button>
</form>
{% if error %}
  <p style="color:red;">{{ error }}</p>
{% endif %}
"""

def fetch_weather(city, units="metric"):
    if not API_KEY:
        return None, "API key is missing."

    params = {"q": city, "appid": API_KEY}
    if units in ("metric", "imperial"):
        params["units"] = units

    try:
        r = requests.get(BASE_URL, params=params, timeout=10)
        r.raise_for_status()
        data = r.json()
        if data.get("cod") != 200:
            return None, data.get("message", "API error")
        return data, None
    except requests.RequestException as e:
        return None, str(e)

@app.route("/", methods=["GET"])
def index():
    city = request.args.get("city", "London")
    units = request.args.get("units", "metric")
    data, error = fetch_weather(city, units)
    if data:
        name = data["name"]
        country = data["sys"]["country"]
        desc = data["weather"][0]["description"].title()
        temp = data["main"]["temp"]
        feels_like = data["main"]["feels_like"]
        humidity = data["main"]["humidity"]
        wind = data["wind"]["speed"]
        timestamp = datetime.fromtimestamp(data["dt"]).strftime("%Y-%m-%d %H:%M:%S")
        unit_symbol = "°C" if units == "metric" else "°F" if units == "imperial" else "K"
        speed_unit = "m/s" if units in ("metric", "") else "mph"
        return render_template_string(
            TEMPLATE,
            name=name,
            country=country,
            desc=desc,
            temp=temp,
            feels_like=feels_like,
            humidity=humidity,
            wind=wind,
            timestamp=timestamp,
            unit=unit_symbol,
            speed_unit=speed_unit,
            units=units,
            error=None,
        )
    else:
        return render_template_string(
            TEMPLATE,
            name=city,
            country="",
            desc="",
            temp="",
            feels_like="",
            humidity="",
            wind="",
            timestamp="",
            unit="",
            speed_unit="",
            units=units,
            error=error,
        )

if __name__ == "__main__":
    app.run(debug=True)

Run with

export OWM_API_KEY=your_api_key_here
python weather_web.py

Then visit http://127.0.0.1:5000 in the browser

Enhancements & Next Steps

Security & Best Practices

Conclusion

This weather application demonstrates how to integrate external REST APIs in Python, parse JSON responses, handle errors, and optionally build a lightweight web UI. It's a great stepping stone toward more complex data-driven apps.

Output

Output screenshot