<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Athar Ahmad</title>
    <description>The latest articles on DEV Community by Athar Ahmad (@worldweatheronline).</description>
    <link>https://dev.to/worldweatheronline</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2445908%2F68c087e8-930a-4c84-b5a7-2846dcc09994.png</url>
      <title>DEV Community: Athar Ahmad</title>
      <link>https://dev.to/worldweatheronline</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/worldweatheronline"/>
    <language>en</language>
    <item>
      <title>How to Build a 5-Day Weather Dashboard in Python (Free API)</title>
      <dc:creator>Athar Ahmad</dc:creator>
      <pubDate>Tue, 24 Feb 2026 08:10:00 +0000</pubDate>
      <link>https://dev.to/worldweatheronline/how-to-build-a-5-day-weather-dashboard-in-python-free-api-46f7</link>
      <guid>https://dev.to/worldweatheronline/how-to-build-a-5-day-weather-dashboard-in-python-free-api-46f7</guid>
      <description>&lt;p&gt;&lt;em&gt;In this tutorial you’ll build a real terminal weather dashboard — with forecasts, humidity, wind speed, and emoji weather icons — using Python and a free weather API. No experience needed.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What You’ll Build
&lt;/h2&gt;

&lt;p&gt;By the end of this tutorial, you’ll have a working Python script that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shows &lt;strong&gt;current weather conditions&lt;/strong&gt; for any city in the world&lt;/li&gt;
&lt;li&gt;Displays a &lt;strong&gt;5-day forecast&lt;/strong&gt; in a clean table format&lt;/li&gt;
&lt;li&gt;Includes temperature, humidity, wind speed, rain chance, and weather icons&lt;/li&gt;
&lt;li&gt;Accepts &lt;strong&gt;any city&lt;/strong&gt; as a command-line argument&lt;/li&gt;
&lt;li&gt;Takes about &lt;strong&gt;20 minutes&lt;/strong&gt; to complete&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what the output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;──────────────────────────────────────────────────
📍 London, United Kingdom — Right Now
──────────────────────────────────────────────────
⛅ Partly Cloudy
🌡 Temperature : 14°C / 57°F (Feels like 12°C)
💧 Humidity : 78%
💨 Wind : 15 mph SW
👁 Visibility : 10 km
☀ UV Index : 2
──────────────────────────────────────────────────

📅 Forecast

Date Conditions High Low Rain%
─────────────────────────────────────────────────────────────
Mon 24 Feb ⛅ Partly Cloudy 14°C 9°C 20%
Tue 25 Feb 🌧 Light Rain 11°C 7°C 85%
Wed 26 Feb ☁ Overcast 12°C 8°C 40%
Thu 27 Feb ☀ Sunny 16°C 9°C 5%
Fri 28 Feb ⛅ Partly Cloudy 15°C 10°C 15%

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What You’ll Need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.8 or newer&lt;/strong&gt; — &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;download here&lt;/a&gt; if you don’t have it&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;free API key&lt;/strong&gt; from World Weather Online — takes 2 minutes to get, no credit card needed&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;code editor&lt;/strong&gt; — &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; is great if you don’t have one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it. Let’s go.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Get Your Free API Key
&lt;/h2&gt;

&lt;p&gt;First, you need an API key. Think of it like a password that lets your code talk to the weather service.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;&lt;a href="https://www.worldweatheronline.com/weather-api/" rel="noopener noreferrer"&gt;worldweatheronline.com/weather-api&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Sign Up Free&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fill in your name and email address&lt;/li&gt;
&lt;li&gt;Check your inbox and click the confirmation link&lt;/li&gt;
&lt;li&gt;Log in and copy your API key from the dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep this key handy — you’ll need it in Step 4. The free plan gives you &lt;strong&gt;500 API calls per day&lt;/strong&gt; , which is more than enough for learning and personal projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Install the Required Libraries
&lt;/h2&gt;

&lt;p&gt;We need two Python libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/strong&gt; — makes it easy to call web APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;rich&lt;/code&gt;&lt;/strong&gt; — makes our terminal output look beautiful (this one is optional but makes a big difference)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open your terminal (or Command Prompt on Windows) and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install requests rich

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re on a Mac and get a permission error, try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install requests rich

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check they installed correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -c "import requests, rich; print('All good!')"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;All good!&lt;/code&gt; printed. If you see an error, make sure you’re using the same Python version you just installed the packages for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Create Your Project File
&lt;/h2&gt;

&lt;p&gt;Create a new folder on your computer called &lt;code&gt;weather-dashboard&lt;/code&gt;, then inside it create a file called &lt;code&gt;weather.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your folder should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;weather-dashboard/
└── weather.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;weather.py&lt;/code&gt; in your code editor and leave it empty for now — we’ll fill it in as we go.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Set Up Your API Key Safely
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Never paste your API key directly into your code, especially if you plan to share it on GitHub. Instead, we store it as an &lt;strong&gt;environment variable&lt;/strong&gt; — a value your computer holds in memory, separate from your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Mac/Linux&lt;/strong&gt; , open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export WWO_API_KEY="paste_your_key_here"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows (Command Prompt)&lt;/strong&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set WWO_API_KEY=paste_your_key_here

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows (PowerShell)&lt;/strong&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$env:WWO_API_KEY="paste_your_key_here"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You’ll need to run this command each time you open a new terminal window. To make it permanent, add it to your &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; file on Mac/Linux, or set it in System Environment Variables on Windows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let’s read it in Python. Add this to the top of your &lt;code&gt;weather.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os

API_KEY = os.environ.get("WWO_API_KEY", "your_api_key_here")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line checks for your environment variable. If it doesn’t find one, it falls back to the text &lt;code&gt;"your_api_key_here"&lt;/code&gt; — which our script will detect and show a helpful error message for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Make Your First API Call
&lt;/h2&gt;

&lt;p&gt;Now the fun part. Let’s fetch some weather data.&lt;/p&gt;

&lt;p&gt;The World Weather Online API works like this: you send it a web request with your location and API key, and it sends back JSON data (a structured text format) with all the weather information.&lt;/p&gt;

&lt;p&gt;Add this to your &lt;code&gt;weather.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
import os

API_KEY = os.environ.get("WWO_API_KEY", "your_api_key_here")
BASE_URL = "https://api.worldweatheronline.com/premium/v1/weather.ashx"

def get_weather(location, days=5):
    """Fetch weather data from the API."""

    params = {
        "key": API_KEY,
        "q": location, # city name or "lat,lon" coordinates
        "format": "json", # we want JSON back, not XML
        "num_of_days": days, # how many days of forecast
        "tp": 24, # 24-hour intervals (daily summary)
        "includelocation": "yes", # include the matched location name
        "cc": "yes", # include current conditions
    }

    response = requests.get(BASE_URL, params=params)
    data = response.json()
    return data["data"]

# Test it!
weather = get_weather("London")
print(weather["current_condition"][0]["temp_C"], "°C")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python weather.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is working, you’ll see the current temperature in London printed to the screen — something like &lt;code&gt;14 °C&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What just happened?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We built a URL with your API key and location attached&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;requests.get()&lt;/code&gt; sent that URL to the World Weather Online server&lt;/li&gt;
&lt;li&gt;The server sent back a big chunk of JSON data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.json()&lt;/code&gt; converted that into a Python dictionary&lt;/li&gt;
&lt;li&gt;We dug into &lt;code&gt;data["current_condition"][0]["temp_C"]&lt;/code&gt; to get the temperature&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 6 — Understand the API Response
&lt;/h2&gt;

&lt;p&gt;Before we build the dashboard, let’s understand what data the API gives us. Add this temporarily to see the full response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
weather = get_weather("London")
print(json.dumps(weather, indent=2))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll see something like this (shortened):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "current_condition": [
    {
      "temp_C": "14",
      "temp_F": "57",
      "FeelsLikeC": "12",
      "humidity": "78",
      "windspeedMiles": "15",
      "winddir16Point": "SW",
      "uvIndex": "2",
      "visibility": "10",
      "weatherDesc": [{ "value": "Partly Cloudy" }]
    }
  ],
  "weather": [
    {
      "date": "2026-02-24",
      "maxtempC": "14",
      "mintempC": "9",
      "hourly": [
        {
          "weatherDesc": [{ "value": "Partly Cloudy" }],
          "chanceofrain": "20",
          "windspeedMiles": "15"
        }
      ]
    }
  ],
  "nearest_area": [
    {
      "areaName": [{ "value": "London" }],
      "country": [{ "value": "United Kingdom" }]
    }
  ]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structure has three main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;current_condition&lt;/code&gt;&lt;/strong&gt; — what the weather is like right now&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;weather&lt;/code&gt;&lt;/strong&gt; — an array of daily forecasts (one per day)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;nearest_area&lt;/code&gt;&lt;/strong&gt; — the location the API matched to your query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Delete the debug print and let’s build the display.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7 — Add Weather Icons
&lt;/h2&gt;

&lt;p&gt;Weather is visual — let’s make our output feel that way. We’ll map common weather descriptions to emojis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WEATHER_ICONS = {
    "sunny": "☀",
    "clear": "🌙",
    "partly cloudy": "⛅",
    "cloudy": "☁",
    "overcast": "☁",
    "mist": "🌫",
    "fog": "🌫",
    "rain": "🌧",
    "drizzle": "🌦",
    "snow": "❄",
    "sleet": "🌨",
    "thunder": "⛈",
    "blizzard": "🌨",
}

def get_icon(description):
    """Return an emoji for a weather description."""
    desc = description.lower()
    for keyword, icon in WEATHER_ICONS.items():
        if keyword in desc:
            return icon
    return "🌡" # default if nothing matches

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function lowercases the description and checks if any of our keywords appear in it. So &lt;code&gt;"Light rain showers"&lt;/code&gt; will match &lt;code&gt;"rain"&lt;/code&gt; and return &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8jv1j5dpxbqbobcpaq6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8jv1j5dpxbqbobcpaq6x.png" alt="🌧" width="72" height="72"&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8 — Display Current Conditions
&lt;/h2&gt;

&lt;p&gt;Now let’s build the function that shows the current weather panel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def display_current(current, location_name):
    """Print current weather conditions."""

    desc = current["weatherDesc"][0]["value"]
    icon = get_icon(desc)
    temp_c = current["temp_C"]
    temp_f = current["temp_F"]
    feels = current["FeelsLikeC"]
    humid = current["humidity"]
    wind = current["windspeedMiles"]
    wdir = current["winddir16Point"]
    uv = current["uvIndex"]
    vis = current["visibility"]

    print("\n" + "─" * 50)
    print(f"📍 {location_name} — Right Now")
    print("─" * 50)
    print(f"{icon} {desc}")
    print(f"🌡 Temperature : {temp_c}°C / {temp_f}°F (Feels like {feels}°C)")
    print(f"💧 Humidity : {humid}%")
    print(f"💨 Wind : {wind} mph {wdir}")
    print(f"👁 Visibility : {vis} km")
    print(f"☀ UV Index : {uv}")
    print("─" * 50)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What’s happening here?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;current["weatherDesc"][0]["value"]&lt;/code&gt; — the description is nested inside a list and a dictionary, so we dig into it with &lt;code&gt;[0]["value"]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We use Python &lt;strong&gt;f-strings&lt;/strong&gt; (the &lt;code&gt;f"..."&lt;/code&gt; strings) to neatly slot variables into our text&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"─" * 50&lt;/code&gt; repeats the dash character 50 times to make a dividing line&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 9 — Display the Forecast Table
&lt;/h2&gt;

&lt;p&gt;Now for the 5-day forecast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from datetime import datetime

def display_forecast(weather_days):
    """Print a multi-day forecast table."""

    print("\n📅 Forecast\n")
    print(f"{'Date':&amp;lt;14} {'Conditions':&amp;lt;25} {'High':&amp;gt;7} {'Low':&amp;gt;7} {'Rain%':&amp;gt;7} {'Wind':&amp;gt;7}")
    print("─" * 75)

    for day in weather_days:
        # Format the date nicely: "Mon 24 Feb"
        date_obj = datetime.strptime(day["date"], "%Y-%m-%d")
        date_fmt = date_obj.strftime("%a %d %b")

        # Get the condition description and icon
        desc = day["hourly"][0]["weatherDesc"][0]["value"]
        icon = get_icon(desc)

        # Get temperatures and other stats
        max_c = day["maxtempC"]
        min_c = day["mintempC"]
        rain = day["hourly"][0].get("chanceofrain", "N/A")
        wind = day["hourly"][0]["windspeedMiles"]

        print(f"{date_fmt:&amp;lt;14} {icon+' '+desc:&amp;lt;25} {max_c+'°C':&amp;gt;7} {min_c+'°C':&amp;gt;7} {rain+'%':&amp;gt;7} {wind:&amp;gt;7}")

    print("─" * 75)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Things to notice:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;f"{'Date':&amp;lt;14}"&lt;/code&gt; — the &lt;code&gt;:&amp;lt;14&lt;/code&gt; part left-aligns the text in a column 14 characters wide. &lt;code&gt;:&amp;gt;7&lt;/code&gt; right-aligns in 7 characters. This keeps everything lined up neatly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;datetime.strptime(day["date"], "%Y-%m-%d")&lt;/code&gt; — converts the date string &lt;code&gt;"2026-02-24"&lt;/code&gt; into a Python date object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.strftime("%a %d %b")&lt;/code&gt; — formats it back as &lt;code&gt;"Mon 24 Feb"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.get("chanceofrain", "N/A")&lt;/code&gt; — safely gets rain chance, defaulting to “N/A” if it’s missing&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 10 — Add Error Handling
&lt;/h2&gt;

&lt;p&gt;Good code handles things going wrong gracefully. What if someone types a city that doesn’t exist? What if the internet is down?&lt;/p&gt;

&lt;p&gt;Replace your &lt;code&gt;get_weather&lt;/code&gt; function with this improved version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_weather(location, days=5):
    """Fetch weather data with error handling."""

    if API_KEY == "your_api_key_here":
        print("❌ Please set your API key!")
        print(" Run: export WWO_API_KEY='your_key_here'")
        print(" Get a free key: https://www.worldweatheronline.com/weather-api/")
        return None

    params = {
        "key": API_KEY, "q": location, "format": "json",
        "num_of_days": days, "tp": 24, "includelocation": "yes", "cc": "yes",
    }

    try:
        response = requests.get(BASE_URL, params=params, timeout=10)
        response.raise_for_status()
        data = response.json()

        if "error" in data.get("data", {}):
            print(f"❌ API Error: {data['data']['error'][0]['msg']}")
            return None

        return data["data"]

    except requests.exceptions.ConnectionError:
        print("❌ No internet connection. Please check your network.")
    except requests.exceptions.Timeout:
        print("❌ The request timed out. Please try again.")
    except requests.exceptions.HTTPError as e:
        print(f"❌ HTTP Error: {e}")

    return None

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What’s new:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;timeout=10&lt;/code&gt; — if the API doesn’t respond in 10 seconds, we give up cleanly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;raise_for_status()&lt;/code&gt; — raises an error if the server returns a 4xx or 5xx status code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;try/except&lt;/code&gt; blocks — catch specific errors and show friendly messages instead of a crash&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 11 — Tie It All Together
&lt;/h2&gt;

&lt;p&gt;Now let’s add the main block that runs everything. Add this at the bottom of your file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import argparse

def main():
    # Set up command-line arguments
    parser = argparse.ArgumentParser(description="World Weather Online — Terminal Dashboard")
    parser.add_argument("--location", "-l", default="London",
                        help="City name or coordinates (default: London)")
    parser.add_argument("--days", "-d", type=int, default=5,
                        help="Number of forecast days, 1-7 (default: 5)")
    args = parser.parse_args()

    print(f"\n🌍 Fetching weather for {args.location}...")

    data = get_weather(args.location, args.days)
    if not data:
        return # Error was already printed inside get_weather

    # Get the matched location name from the API
    try:
        area = data["nearest_area"][0]["areaName"][0]["value"]
        country = data["nearest_area"][0]["country"][0]["value"]
        location_name = f"{area}, {country}"
    except (KeyError, IndexError):
        location_name = args.location # fall back to what the user typed

    display_current(data["current_condition"][0], location_name)
    display_forecast(data["weather"])

    print("\nData by World Weather Online — https://www.worldweatheronline.com\n")

if __name__ == " __main__":
    main()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;if __name__ == " __main__":&lt;/code&gt; line means “only run &lt;code&gt;main()&lt;/code&gt; if this file is being run directly” — a Python best practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 12 — Run Your Dashboard!
&lt;/h2&gt;

&lt;p&gt;Save the file and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Default (London, 5 days)
python weather.py

# Choose a city
python weather.py --location "Tokyo"
python weather.py --location "New York"
python weather.py -l Paris

# Choose number of days (1-7)
python weather.py -l Sydney -d 3

# Works with coordinates too
python weather.py -l "51.5,-0.1"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your full weather dashboard in the terminal!&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Make It Even Prettier with Rich
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;rich&lt;/code&gt; library we installed earlier makes terminal output look really polished. Here’s how to add it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich import box

console = Console()

def display_current_rich(current, location_name):
    """Fancy version using rich."""
    desc = current["weatherDesc"][0]["value"]
    icon = get_icon(desc)

    content = (
        f"[bold]{icon} {desc}[/bold]\n\n"
        f"🌡 Temperature : [bold cyan]{current['temp_C']}°C[/bold cyan] / "
        f"{current['temp_F']}°F (Feels like {current['FeelsLikeC']}°C)\n"
        f"💧 Humidity : {current['humidity']}%\n"
        f"💨 Wind : {current['windspeedMiles']} mph {current['winddir16Point']}\n"
        f"👁 Visibility : {current['visibility']} km\n"
        f"☀ UV Index : {current['uvIndex']}"
    )

    console.print(Panel(
        content,
        title=f"[bold yellow]📍 {location_name} — Right Now[/bold yellow]",
        border_style="blue"
    ))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rich uses tags like &lt;code&gt;[bold]&lt;/code&gt;, &lt;code&gt;[cyan]&lt;/code&gt;, and &lt;code&gt;[bold yellow]&lt;/code&gt; to add colour and style — similar to HTML but for the terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Complete Script
&lt;/h2&gt;

&lt;p&gt;Here’s the full &lt;code&gt;weather.py&lt;/code&gt; all in one place, ready to copy and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
import argparse
import os
from datetime import datetime

API_KEY = os.environ.get("WWO_API_KEY", "your_api_key_here")
BASE_URL = "https://api.worldweatheronline.com/premium/v1/weather.ashx"

WEATHER_ICONS = {
    "sunny": "☀", "clear": "🌙", "partly cloudy": "⛅",
    "cloudy": "☁", "overcast": "☁", "mist": "🌫",
    "fog": "🌫", "rain": "🌧", "drizzle": "🌦",
    "snow": "❄", "sleet": "🌨", "thunder": "⛈", "blizzard": "🌨",
}

def get_icon(description):
    desc = description.lower()
    for keyword, icon in WEATHER_ICONS.items():
        if keyword in desc:
            return icon
    return "🌡"

def get_weather(location, days=5):
    if API_KEY == "your_api_key_here":
        print("❌ Set your API key: export WWO_API_KEY='your_key'")
        print(" Get one free: https://www.worldweatheronline.com/weather-api/")
        return None
    params = {
        "key": API_KEY, "q": location, "format": "json",
        "num_of_days": days, "tp": 24, "includelocation": "yes", "cc": "yes",
    }
    try:
        r = requests.get(BASE_URL, params=params, timeout=10)
        r.raise_for_status()
        data = r.json().get("data", {})
        if "error" in data:
            print(f"❌ {data['error'][0]['msg']}")
            return None
        return data
    except requests.exceptions.ConnectionError:
        print("❌ No internet connection.")
    except requests.exceptions.Timeout:
        print("❌ Request timed out.")
    return None

def display_current(current, location_name):
    desc = current["weatherDesc"][0]["value"]
    icon = get_icon(desc)
    print("\n" + "─" * 50)
    print(f"📍 {location_name} — Right Now")
    print("─" * 50)
    print(f"{icon} {desc}")
    print(f"🌡 Temperature : {current['temp_C']}°C / {current['temp_F']}°F (Feels like {current['FeelsLikeC']}°C)")
    print(f"💧 Humidity : {current['humidity']}%")
    print(f"💨 Wind : {current['windspeedMiles']} mph {current['winddir16Point']}")
    print(f"👁 Visibility : {current['visibility']} km")
    print(f"☀ UV Index : {current['uvIndex']}")
    print("─" * 50)

def display_forecast(weather_days):
    print("\n📅 Forecast\n")
    print(f"{'Date':&amp;lt;14} {'Conditions':&amp;lt;25} {'High':&amp;gt;7} {'Low':&amp;gt;7} {'Rain%':&amp;gt;7} {'Wind':&amp;gt;7}")
    print("─" * 75)
    for day in weather_days:
        date_fmt = datetime.strptime(day["date"], "%Y-%m-%d").strftime("%a %d %b")
        desc = day["hourly"][0]["weatherDesc"][0]["value"]
        icon = get_icon(desc)
        rain = day["hourly"][0].get("chanceofrain", "N/A")
        wind = day["hourly"][0]["windspeedMiles"]
        print(f"{date_fmt:&amp;lt;14} {icon+' '+desc:&amp;lt;25} {day['maxtempC']+'°C':&amp;gt;7} {day['mintempC']+'°C':&amp;gt;7} {rain+'%':&amp;gt;7} {wind:&amp;gt;7}")
    print("─" * 75)

def main():
    parser = argparse.ArgumentParser(description="World Weather Online — Terminal Dashboard")
    parser.add_argument("--location", "-l", default="London")
    parser.add_argument("--days", "-d", type=int, default=5)
    args = parser.parse_args()

    print(f"\n🌍 Fetching weather for {args.location}...")
    data = get_weather(args.location, args.days)
    if not data:
        return

    try:
        area = data["nearest_area"][0]["areaName"][0]["value"]
        country = data["nearest_area"][0]["country"][0]["value"]
        location_name = f"{area}, {country}"
    except (KeyError, IndexError):
        location_name = args.location

    display_current(data["current_condition"][0], location_name)
    display_forecast(data["weather"])
    print("\nData by World Weather Online — https://www.worldweatheronline.com\n")

if __name__ == " __main__":
    main()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What You Learned
&lt;/h2&gt;

&lt;p&gt;In this tutorial you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Made your first call to a &lt;strong&gt;real REST API&lt;/strong&gt; using Python&lt;/li&gt;
&lt;li&gt;Worked with &lt;strong&gt;JSON data&lt;/strong&gt; and navigated nested dictionaries&lt;/li&gt;
&lt;li&gt;Used &lt;strong&gt;f-strings&lt;/strong&gt; and string formatting to create aligned tables&lt;/li&gt;
&lt;li&gt;Added &lt;strong&gt;error handling&lt;/strong&gt; so your script fails gracefully&lt;/li&gt;
&lt;li&gt;Used &lt;strong&gt;argparse&lt;/strong&gt; to accept command-line arguments&lt;/li&gt;
&lt;li&gt;Stored secrets safely using &lt;strong&gt;environment variables&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What to Try Next
&lt;/h2&gt;

&lt;p&gt;Now that you have a working dashboard, here are some ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Export to CSV&lt;/strong&gt; — add &lt;code&gt;import csv&lt;/code&gt; and write the forecast data to a file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email alerts&lt;/strong&gt; — send yourself a morning weather summary using Python’s &lt;code&gt;smtplib&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple cities&lt;/strong&gt; — loop over a list of cities and compare them side by side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historical data&lt;/strong&gt; — use the &lt;a href="https://www.worldweatheronline.com/weather-api/api/historical-weather-api.aspx" rel="noopener noreferrer"&gt;Historical Weather API&lt;/a&gt; to analyse past weather trends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marine forecasts&lt;/strong&gt; — swap to the &lt;a href="https://www.worldweatheronline.com/weather-api/api/marine-weather-api.aspx" rel="noopener noreferrer"&gt;Marine API&lt;/a&gt; for wave height and tide data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Get Your Free API Key
&lt;/h2&gt;

&lt;p&gt;Everything in this tutorial works on the &lt;strong&gt;free plan&lt;/strong&gt; — 500 API calls per day, no credit card required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u5z0tdpxjfmq6pjqqdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u5z0tdpxjfmq6pjqqdr.png" alt="👉" width="72" height="72"&gt;&lt;/a&gt; &lt;strong&gt;&lt;a href="https://www.worldweatheronline.com/weather-api/" rel="noopener noreferrer"&gt;Sign up free at worldweatheronline.com/weather-api&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions or got stuck? Drop a comment below — I’ll help you out.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;python&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt; &lt;code&gt;api&lt;/code&gt; &lt;code&gt;weather&lt;/code&gt; &lt;code&gt;tutorial&lt;/code&gt; &lt;code&gt;programming&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.worldweatheronline.com/weather/how-to-build-a-5-day-weather-dashboard-in-python-free-api/" rel="noopener noreferrer"&gt;How to Build a 5-Day Weather Dashboard in Python (Free API)&lt;/a&gt; appeared first on &lt;a href="https://blog.worldweatheronline.com" rel="noopener noreferrer"&gt;Weather Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>weather</category>
    </item>
    <item>
      <title>Iceland in Late Spring: Waterfalls, Midnight Sun &amp; Unpredictable Skies</title>
      <dc:creator>Athar Ahmad</dc:creator>
      <pubDate>Sun, 22 Feb 2026 18:13:25 +0000</pubDate>
      <link>https://dev.to/worldweatheronline/iceland-in-late-spring-waterfalls-midnight-sun-unpredictable-skies-4b41</link>
      <guid>https://dev.to/worldweatheronline/iceland-in-late-spring-waterfalls-midnight-sun-unpredictable-skies-4b41</guid>
      <description>&lt;p&gt;Iceland is one of the most beautiful countries in the world, with remarkable waterfalls and memorable spring landscapes. Late spring, especially between May and June, proves to be one of the most intriguing times to …&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.worldweatheronline.com/travel/iceland-in-late-spring-waterfalls-midnight-sun-unpredictable-skies/" rel="noopener noreferrer"&gt;Iceland in Late Spring: Waterfalls, Midnight Sun &amp;amp; Unpredictable Skies&lt;/a&gt; appeared first on &lt;a href="https://blog.worldweatheronline.com" rel="noopener noreferrer"&gt;Weather Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>europe</category>
      <category>iceland</category>
      <category>travel</category>
      <category>weather</category>
    </item>
  </channel>
</rss>
