---
title: "How to Build a Weather App With a Free Public API in 2025"
description: "Learn how to build a weather app from scratch using free public APIs like OpenWeatherMap. Step-by-step JavaScript tutorial with code snippets, styling tips, and deployment."
date: 2025-04-11
tags:
  - "weather app"
  - "public api"
  - "javascript tutorial"
  - "openweathermap"
  - "frontend project"
  - "fetch api"
  - "geolocation"
  - "beginner friendly"
authors:
  - "Cojocaru David"
  - "ChatGPT"
slug: "how-to-build-a-weather-app-with-a-public-api"
updatedDate: 2025-08-13
---

# How to Build a Weather App With a Free Public API in 2025 (Even If You're a Beginner)

Hey there! So you want to build a weather app that actually works? I get it. There's something oddly satisfying about typing "London" and instantly seeing "19°C, partly cloudy" pop up on your screen.

Here's the thing **you don't need a CS degree** or a server farm. Just a free public weather API, a bit of vanilla JavaScript, and about 30 minutes of focused work. I'll walk you through every single step, from grabbing the API key to pushing your finished app live on the internet.

Ready? Let's make some clouds appear... on your screen.

## Why Bother With a Weather App? (Spoiler: It's the Perfect Weekend Project)

I built my first weather app on a rainy Saturday. Took me two cups of coffee and one existential crisis when the API returned "400 Bad Request." But once it worked? Magic.

Here's why this project rocks:

* **It's beginner-friendly** - No databases, no backend, no fancy frameworks
* **You learn real skills** - API calls, async JavaScript, geolocation, CSS grid
* **It's actually useful** - Your mom will finally understand what you do for a living
* **Great portfolio piece** - Recruiters love seeing practical projects

Plus, let's be honest, it's way cooler than another to-do list.

## Picking the Right Weather API (The Free Ones That Don't Suck)

You have options. Oh boy, do you have options. After testing six different services, here are the three that won't make you want to throw your laptop out the window:

### 1. OpenWeatherMap (My Go-To)
- **Free tier**: 1,000 calls/day
- **What you get**: Current weather, 5-day forecast, air quality
- **Signup**: Takes 2 minutes, instant API key
- **Quirk**: Temperature comes in Kelvin by default (because why not)

### 2. WeatherAPI
- **Free tier**: 1 million calls/month
- **What you get**: Super detailed forecasts, astronomy data
- **Bonus**: Built-in weather icons
- **Catch**: Requires email verification (they'll send you weather puns)

### 3. WeatherAPI.com (Yes, another one)
- **Free tier**: 100 calls/day
- **Best for**: Historical weather data
- **Use case**: "Remember that day last July when..."

For this tutorial, we're using **OpenWeatherMap**. Why? Because it's like the McDonald's of weather APIs everyone knows it, it's everywhere, and it just works.

## Before We Start: The Shopping List

Don't worry, this isn't one of those tutorials that requires 47 dependencies and a PhD in configuration. Here's literally all you need:

**The Basics:**
- A computer (I'm assuming you have one)
- Internet connection (obviously)
- A code editor (VS Code is free and awesome)
- 30-45 minutes of uninterrupted time

**The Technical Stuff:**
- Basic HTML/CSS/JavaScript knowledge (if you can make a button change color, you're good)
- A free OpenWeatherMap account (we'll get this in 2 minutes)
- A browser (Chrome, Firefox, Safari whatever floats your boat)

**Optional but Nice:**
- Live Server extension for VS Code (auto-refresh is life)
- A second monitor (or just split your screen)

## Step 1: Getting Your Free API Key (Don't Skip This)

I know, I know. Another signup process. But trust me, this one's painless.

Here's the 2-minute version:

1. Go to [openweathermap.org/api](https://openweathermap.org/api)
2. Click "Sign Up" (it's free, pinky promise)
3. Fill in email, password, company name (put "Learning" or your name)
4. Check your email for verification
5. Log in and go to "API Keys" section
6. Copy that long string of letters and numbers

**Pro tip**: Save your API key in a text file called `api-key.txt`. You'll need it in about 5 minutes.

## Step 2: Setting Up Your Project Files (The Folder Structure That Won't Confuse You)

Let's keep this simple. Create a new folder on your desktop called `weather-app`. Inside, make three files:

```
weather-app/
├── index.html
├── style.css
└── script.js
```

That's it. No package.json, no node_modules, no webpack config. We're going old school, baby.

## Step 3: The HTML Foundation (Copy-Paste Friendly)

Open `index.html` and paste this:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Weather App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>🌤️ Weather Check</h1>
        
        <div class="search-box">
            <input type="text" id="city-input" placeholder="Enter city name...">
            <button id="search-btn">Search</button>
            <button id="location-btn">Use My Location</button>
        </div>
        
        <div id="weather-info" class="hidden">
            <h2 id="city-name">--</h2>
            <div class="weather-details">
                <p>🌡️ Temperature: <span id="temperature">--</span>°C</p>
                <p>💧 Humidity: <span id="humidity">--</span>%</p>
                <p>💨 Wind: <span id="wind-speed">--</span> km/h</p>
                <p>☁️ Condition: <span id="description">--</span></p>
            </div>
            <img id="weather-icon" src="" alt="Weather icon">
        </div>
        
        <div id="error-message" class="hidden">
            <p>😅 Oops! City not found. Try again?</p>
        </div>
    </div>
    
    <script src="script.js"></script>
</body>
</html>
```

Notice the `hidden` classes? We'll toggle these with JavaScript. Clean and simple.

## Step 4: Making It Pretty (CSS That Doesn't Suck)

Open `style.css` and let's make this thing Instagram-worthy:

```css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.container {
    background: rgba(255, 255, 255, 0.9);
    padding: 40px;
    border-radius: 20px;
    box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
    max-width: 400px;
    width: 100%;
}

h1 {
    text-align: center;
    color: #333;
    margin-bottom: 30px;
}

.search-box {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 30px;
}

input, button {
    padding: 12px;
    border: none;
    border-radius: 8px;
    font-size: 16px;
}

input {
    background: #f5f5f5;
    outline: none;
}

button {
    background: #667eea;
    color: white;
    cursor: pointer;
    transition: background 0.3s;
}

button:hover {
    background: #5a6fd8;
}

.weather-details {
    text-align: center;
    margin: 20px 0;
}

.weather-details p {
    margin: 10px 0;
    font-size: 18px;
    color: #555;
}

#weather-icon {
    display: block;
    margin: 20px auto;
    width: 100px;
    height: 100px;
}

.hidden {
    display: none;
}

#error-message {
    text-align: center;
    color: #e74c3c;
    font-weight: bold;
}
```

Feel free to tweak colors. I went with a purple gradient because it looks like a sunset. Or a grape soda. Either works.

## Step 5: The JavaScript Magic (Where It All Comes Together)

Now for the fun part. Open `script.js` and let's write some code that actually does stuff:

```javascript
// Replace 'YOUR_API_KEY' with your actual key from OpenWeatherMap
const API_KEY = 'YOUR_API_KEY';
const API_URL = 'https://api.openweathermap.org/data/2.5/weather';

// Get DOM elements
const cityInput = document.getElementById('city-input');
const searchBtn = document.getElementById('search-btn');
const locationBtn = document.getElementById('location-btn');
const weatherInfo = document.getElementById('weather-info');
const errorMessage = document.getElementById('error-message');

// Event listeners
searchBtn.addEventListener('click', searchWeather);
locationBtn.addEventListener('click', getCurrentLocation);
cityInput.addEventListener('keypress', (e) => {
    if (e.key === 'Enter') searchWeather();
});

// Main search function
async function searchWeather() {
    const city = cityInput.value.trim();
    
    if (!city) {
        showError('Please enter a city name');
        return;
    }
    
    try {
        showLoading();
        const response = await fetch(`${API_URL}?q=${city}&appid=${API_KEY}&units=metric`);
        
        if (!response.ok) {
            throw new Error('City not found');
        }
        
        const data = await response.json();
        displayWeather(data);
    } catch (error) {
        showError(error.message);
    }
}

// Geolocation function
function getCurrentLocation() {
    if (!navigator.geolocation) {
        showError('Geolocation is not supported by your browser');
        return;
    }
    
    showLoading();
    
    navigator.geolocation.getCurrentPosition(
        async (position) => {
            const { latitude, longitude } = position.coords;
            
            try {
                const response = await fetch(
                    `${API_URL}?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
                );
                
                if (!response.ok) {
                    throw new Error('Could not fetch weather for your location');
                }
                
                const data = await response.json();
                displayWeather(data);
            } catch (error) {
                showError(error.message);
            }
        },
        () => {
            showError('Unable to retrieve your location');
        }
    );
}

// Display weather data
function displayWeather(data) {
    // Hide error if it was shown
    errorMessage.classList.add('hidden');
    
    // Update DOM elements
    document.getElementById('city-name').textContent = `${data.name}, ${data.sys.country}`;
    document.getElementById('temperature').textContent = Math.round(data.main.temp);
    document.getElementById('humidity').textContent = data.main.humidity;
    document.getElementById('wind-speed').textContent = Math.round(data.wind.speed * 3.6); // Convert m/s to km/h
    document.getElementById('description').textContent = data.weather[0].description;
    
    // Set weather icon
    const iconCode = data.weather[0].icon;
    const iconUrl = `https://openweathermap.org/img/wn/${iconCode}@2x.png`;
    document.getElementById('weather-icon').src = iconUrl;
    
    // Show weather info
    weatherInfo.classList.remove('hidden');
}

// Helper functions
function showLoading() {
    weatherInfo.classList.add('hidden');
    errorMessage.classList.add('hidden');
}

function showError(message) {
    weatherInfo.classList.add('hidden');
    errorMessage.classList.remove('hidden');
    errorMessage.querySelector('p').textContent = `😅 ${message}`;
}
```

**Important**: Don't forget to replace `YOUR_API_KEY` with your actual API key!

## Step 6: Testing Your Masterpiece

Time to see if this thing actually works:

1. Save all your files
2. Open `index.html` in your browser
3. Try searching for "London" or "Tokyo"
4. Click the "Use My Location" button (allow location access when prompted)

If you see weather data, congratulations! You just built a working weather app. If not, check the console for errors usually it's a typo or the API key.

## Common Issues & Quick Fixes

**Problem**: Getting "401 Unauthorized"
- **Solution**: Your API key is wrong or not activated yet. Wait 10 minutes after signup.

**Problem**: Icons not showing
- **Solution**: Check if the icon URL is correct. Should be `https://openweathermap.org/img/wn/10d@2x.png`

**Problem**: Temperature shows in Fahrenheit
- **Solution**: Make sure you added `&units=metric` to the API URL

## Leveling Up: Cool Features to Add Later

Once your basic app works, here are some fun additions:

* **5-day forecast** - Use the `/forecast` endpoint
* **Dark mode toggle** - Because everything needs dark mode
* **Favorite cities** - Save searches in localStorage
* **Weather animations** - Add rain drops or sun rays
* **Voice search** - Use Web Speech API (gets you bonus points)

## Deploying Your App (Sharing Is Caring)

Your app works locally, but let's get it online so you can send the link to your friends and watch them be mildly impressed.

**Option 1: Netlify (Easiest)**
1. Go to [netlify.com](https://netlify.com)
2. Drag your entire `weather-app` folder to the deploy area
3. Get a live URL in 30 seconds

**Option 2: Vercel (Also Easy)**
1. Install Vercel CLI: `npm i -g vercel`
2. In your project folder: `vercel`
3. Follow the prompts

**Option 3: GitHub Pages (For the GitHub Crowd)**
1. Push your code to GitHub
2. Go to repository Settings → Pages
3. Select source branch → Save

## Wrapping Up: You Did It!

Look at you! You just:
- ✓ Learned how to work with a real API
- ✓ Built something useful with vanilla JavaScript
- ✓ Used modern async/await syntax
- ✓ Implemented geolocation
- ✓ Deployed a live app

Not bad for a day's work, right?

> _"The best way to predict the future is to build it." - Peter Drucker_

#weatherapp #javascript #api #beginners #webdev #coding #tutorial