How to Build a Weather Extension Without an API Key
Most weather tutorials start with "sign up for a free tier API key." Then your project depends on that key staying valid, rate limits not changing, and the provider not getting acquired.
There's a better way.
Open-Meteo: Free weather data, no sign-up
Open-Meteo is an open-source weather API that:
- Requires zero authentication
- Has no rate limits for non-commercial use
- Returns structured JSON
- Uses ECMWF and GFS forecast models
The only thing you provide: latitude and longitude.
The complete fetch
async function getWeather(lat, lon) {
const params = new URLSearchParams({
latitude: lat,
longitude: lon,
current_weather: true,
daily: 'weathercode,temperature_2m_max,temperature_2m_min',
forecast_days: 4,
timezone: 'auto'
});
const res = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`);
if (!res.ok) throw new Error(`Weather API error: ${res.status}`);
return res.json();
}
Getting user location
The Geolocation API is built into browsers. In an extension context:
function getPosition() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
pos => resolve({ lat: pos.coords.latitude, lon: pos.coords.longitude }),
err => reject(err),
{ timeout: 10000 }
);
});
}
Decoding weather codes
Open-Meteo returns WMO weather codes (0-99). Here's a minimal decoder:
const WEATHER_CODES = {
0: { label: 'Clear sky', icon: '☀️' },
1: { label: 'Mainly clear', icon: '🌤️' },
2: { label: 'Partly cloudy', icon: '⛅' },
3: { label: 'Overcast', icon: '☁️' },
45: { label: 'Foggy', icon: '🌫️' },
48: { label: 'Depositing rime fog', icon: '🌫️' },
51: { label: 'Light drizzle', icon: '🌦️' },
61: { label: 'Slight rain', icon: '🌧️' },
71: { label: 'Slight snow', icon: '🌨️' },
80: { label: 'Slight rain showers', icon: '🌦️' },
95: { label: 'Thunderstorm', icon: '⛈️' },
};
function decodeWeather(code) {
return WEATHER_CODES[code] || { label: 'Unknown', icon: '🌡️' };
}
Putting it together in a browser extension
The manifest.json only needs one permission and one external host:
{
"manifest_version": 2,
"permissions": ["storage", "geolocation"],
"content_security_policy": "script-src 'self'; connect-src 'self' https://api.open-meteo.com"
}
Then in your new tab page:
async function init() {
try {
const { lat, lon } = await getPosition();
const data = await getWeather(lat, lon);
renderWeather(data);
} catch (err) {
if (err.code === 1) {
// PERMISSION_DENIED — show fallback
renderOffline();
} else {
renderError(err.message);
}
}
}
Caching to avoid hitting the API on every new tab
Opening a new tab on every keystroke would spam the API. Cache with a TTL:
async function getCachedWeather(lat, lon) {
const cached = await browser.storage.local.get('weatherCache');
const cache = cached.weatherCache;
if (cache && Date.now() - cache.timestamp < 10 * 60 * 1000) {
return cache.data; // 10-minute TTL
}
const data = await getWeather(lat, lon);
await browser.storage.local.set({
weatherCache: { data, timestamp: Date.now() }
});
return data;
}
The result
This is exactly how Weather & Clock Dashboard works — a Firefox new tab replacement that shows live weather, a 3-day forecast, world clocks, and a search bar. No API keys, no accounts, no backend.
All ~500 lines of it are MIT licensed if you want to fork and extend it.
The broader lesson: before reaching for an API-key service, check if there's an open alternative. Open-Meteo isn't the only one — Nominatim for geocoding, OpenStreetMap for maps, Open Notify for ISS location. The ecosystem of no-auth APIs is larger than most people realize.
Top comments (0)