OpenWeatherMap API for Browser Extensions: A Practical Guide
Source: Dev.to
Introduction
If you’re building a browser extension that shows weather data, OpenWeatherMap is the go‑to choice. Their free tier is genuinely useful, the API is well‑documented, and it works well for extensions that call the API on‑demand (rather than from a server).
Free Tier Limits
- 60 calls per minute
- 1,000,000 calls per month
- Access to Current Weather Data API
- Access to 5 Day / 3 Hour Forecast API
For a new‑tab extension, even with 10 000 active users opening 20 tabs per day, you’d need ~200 000 calls per day (~6 M/month). With 10‑minute caching, that drops to ~600 000/month — comfortably under the free limit.
API Key per User
Every user needs their own API key. You cannot bundle a single key in a publicly distributed extension because the key would be extractable from the source code, allowing anyone to burn through your quota.
Storing the User’s Key
// Extension popup/settings page
async function saveApiKey(key) {
await chrome.storage.local.set({ owmApiKey: key });
}
Fetching Weather with the User’s Key
// In newtab.js
async function getWeather(city) {
const { owmApiKey } = await chrome.storage.local.get('owmApiKey');
if (!owmApiKey) {
// Show API key setup prompt
showApiKeyPrompt();
return;
}
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${owmApiKey}&units=metric`;
const response = await fetch(url);
if (!response.ok) {
if (response.status === 401) {
showApiKeyError('Invalid API key');
}
return;
}
return response.json();
}
Benefits
- Keeps user data private (their key, their account)
- No backend needed for distribution
- Prevents API key extraction from the extension
- Places clear quota responsibility on each user
Caching to Reduce Calls
Calling the API on every tab open is wasteful and quickly hits rate limits.
const CACHE_DURATION = 10 * 60 * 1000; // 10 minutes
async function getWeatherWithCache(city) {
const cacheKey = `weather_${city}`;
const cached = JSON.parse(localStorage.getItem(cacheKey) || 'null');
if (cached && (Date.now() - cached.timestamp) {
const date = new Date(item.dt * 1000);
const dayKey = date.toDateString();
const hour = date.getHours();
// Prefer readings around noon (12‑15h)
if (!byDay[dayKey] || (hour >= 12 && hour day !== today)
.slice(0, 3)
.map(([day, item]) => ({
date: day,
temp_min: Math.round(item.main.temp_min),
temp_max: Math.round(item.main.temp_max),
icon: item.weather[0].icon,
description: item.weather[0].main
}));
}
Weather Icons
OpenWeatherMap icons are served at:
https://openweathermap.org/img/wn/{icon}@2x.png
If you prefer custom icons, you can map condition codes to emojis or your own SVGs:
function weatherCodeToEmoji(iconCode) {
const code = iconCode.substring(0, 2); // '01', '02', etc.
const isDay = iconCode.endsWith('d');
const map = {
'01': isDay ? '☀️' : '🌙',
'02': isDay ? '⛅' : '☁️',
'03': '☁️',
'04': '☁️',
'09': '🌧️',
'10': isDay ? '🌦️' : '🌧️',
'11': '⛈️',
'13': '❄️',
'50': '🌫️'
};
return map[code] || '🌡️';
}
Geolocation vs. Manual Input
Auto‑detecting location is ideal but requires user permission and can fail. Provide a fallback to manual city entry.
async function initWeather() {
// Try geolocation first
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
async (pos) => {
const { latitude, longitude } = pos.coords;
const url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiKey}`;
// ... fetch and display
},
() => {
// Geolocation denied or failed
showCityInputForm();
},
{ timeout: 5000, maximumAge: 60 * 60 * 1000 } // Cache for 1 hour
);
} else {
showCityInputForm();
}
}
Production Checklist
- API: OpenWeatherMap free tier, user‑provided key
- Caching: 10‑minute
localStoragecache - Units: Auto‑detect metric/imperial by location, user‑overridable
- Fallback: Manual city entry if geolocation denied
- Error handling:
401– Bad API key404– City not found- Network errors – Show generic retry message
The extension is open source; you can review the implementation for further details.