OpenWeatherMap API for Browser Extensions: A Practical Guide

Published: (May 3, 2026 at 05:46 PM EDT)
4 min read
Source: Dev.to

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 localStorage cache
  • Units: Auto‑detect metric/imperial by location, user‑overridable
  • Fallback: Manual city entry if geolocation denied
  • Error handling:
    • 401 – Bad API key
    • 404 – City not found
    • Network errors – Show generic retry message

The extension is open source; you can review the implementation for further details.

0 views
Back to Blog

Related posts

Read more »

Making my own framework. Any tips?

!Cover image for Making my own framework. Any tips?https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fde...