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:
- Go to openweathermap.org/api
- Click “Sign Up” (it’s free, pinky promise)
- Fill in email, password, company name (put “Learning” or your name)
- Check your email for verification
- Log in and go to “API Keys” section
- 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:
<!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..." aria-label="City input">
<button id="search-btn" aria-label="Search">Search</button>
<button id="location-btn" aria-label="Use my location">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:
* {
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:
// 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:
- Save all your files
- Open
index.html
in your browser - Try searching for “London” or “Tokyo”
- 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)
- Go to netlify.com
- Drag your entire
weather-app
folder to the deploy area - Get a live URL in 30 seconds
Option 2: Vercel (Also Easy)
- Install Vercel CLI:
npm i -g vercel
- In your project folder:
vercel
- Follow the prompts
Option 3: GitHub Pages (For the GitHub Crowd)
- Push your code to GitHub
- Go to repository Settings → Pages
- 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