Technology May 04, 2026 · 5 min read

How to Use the OpenWeatherMap API in a Firefox Extension

How to Use the OpenWeatherMap API in a Firefox Extension Weather data is one of the most commonly requested features in new tab extensions. The Weather & Clock Dashboard uses OpenWeatherMap's free tier. Here's a complete guide. Getting an API Key Sign up at openweathermap...

DE
DEV Community
by Weather Clock Dash
How to Use the OpenWeatherMap API in a Firefox Extension

How to Use the OpenWeatherMap API in a Firefox Extension

Weather data is one of the most commonly requested features in new tab extensions. The Weather & Clock Dashboard uses OpenWeatherMap's free tier. Here's a complete guide.

Getting an API Key

  1. Sign up at openweathermap.org — free tier allows 60 calls/minute, 1M calls/month
  2. Go to API keys in your account dashboard
  3. Copy the default API key (or create a new one)
  4. Keys become active within 10 minutes

Free tier limits: 60 requests/minute, 1,000,000 requests/month — more than enough for a browser extension.

The Current Weather API

const API_KEY = 'your_api_key_here';
const BASE_URL = 'https://api.openweathermap.org/data/2.5';

async function getCurrentWeather(city) {
  const url = `${BASE_URL}/weather?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=imperial`;

  const response = await fetch(url);

  if (!response.ok) {
    if (response.status === 404) throw new Error(`City "${city}" not found`);
    if (response.status === 401) throw new Error('Invalid API key');
    throw new Error(`Weather API error: ${response.status}`);
  }

  return response.json();
}

// Response structure:
// {
//   name: "San Francisco",
//   sys: { country: "US" },
//   weather: [{ main: "Clear", description: "clear sky", icon: "01d" }],
//   main: { temp: 72, feels_like: 70, humidity: 65, temp_min: 68, temp_max: 75 },
//   wind: { speed: 12, deg: 270 },
//   visibility: 10000,
//   dt: 1699123456  // Unix timestamp
// }

Handling Units

OWM supports three unit systems:

const UNITS = {
  imperial: { temp: '°F', speed: 'mph', param: 'imperial' },
  metric:   { temp: '°C', speed: 'm/s', param: 'metric' },
  standard: { temp: 'K',  speed: 'm/s', param: 'standard' }
};

async function getWeather(city, unitSystem = 'imperial') {
  const { param } = UNITS[unitSystem];
  const url = `${BASE_URL}/weather?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=${param}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();

  return {
    city: data.name,
    country: data.sys.country,
    temp: Math.round(data.main.temp),
    feelsLike: Math.round(data.main.feels_like),
    humidity: data.main.humidity,
    description: data.weather[0].description,
    icon: data.weather[0].icon,
    windSpeed: Math.round(data.wind.speed),
    unitLabel: UNITS[unitSystem].temp,
    speedLabel: UNITS[unitSystem].speed,
  };
}

The 3-Day Forecast API

OWM's /forecast endpoint returns 5-day forecasts in 3-hour intervals:

async function getForecast(city, days = 3) {
  const url = `${BASE_URL}/forecast?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=imperial`;
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();

  // Group by day (take the noon reading for each day)
  const dailyMap = new Map();

  data.list.forEach(item => {
    const date = new Date(item.dt * 1000);
    const dayKey = date.toLocaleDateString();
    const hour = date.getHours();

    // Prefer readings around noon (12-15h)
    if (!dailyMap.has(dayKey) || Math.abs(hour - 13) < Math.abs(dailyMap.get(dayKey).hour - 13)) {
      dailyMap.set(dayKey, {
        hour,
        date,
        temp: Math.round(item.main.temp),
        tempMin: Math.round(item.main.temp_min),
        tempMax: Math.round(item.main.temp_max),
        description: item.weather[0].description,
        icon: item.weather[0].icon,
        humidity: item.main.humidity,
      });
    }
  });

  // Skip today, return next N days
  const today = new Date().toLocaleDateString();
  return Array.from(dailyMap.values())
    .filter(d => d.date.toLocaleDateString() !== today)
    .slice(0, days);
}

Weather Icons

OWM provides icon codes like 01d (clear day), 10n (rain night). Use their CDN:

function getIconUrl(iconCode) {
  return `https://openweathermap.org/img/wn/${iconCode}@2x.png`;
}

// Or map to your own emoji/SVG for better control:
const ICON_MAP = {
  '01d': '☀️',   // Clear sky day
  '01n': '🌙',   // Clear sky night
  '02d': '',   // Few clouds day
  '02n': '☁️',   // Few clouds night
  '03d': '☁️',   // Scattered clouds
  '03n': '☁️',
  '04d': '☁️',   // Broken clouds
  '04n': '☁️',
  '09d': '🌧️',  // Shower rain
  '09n': '🌧️',
  '10d': '🌦️',  // Rain day
  '10n': '🌧️',  // Rain night
  '11d': '⛈️',  // Thunderstorm
  '11n': '⛈️',
  '13d': '❄️',  // Snow
  '13n': '❄️',
  '50d': '🌫️',  // Mist
  '50n': '🌫️',
};

Geolocation Fallback

For users who want automatic location detection:

async function getWeatherByCoords(lat, lon) {
  const url = `${BASE_URL}/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}&units=imperial`;
  const res = await fetch(url);
  return res.json();
}

async function autoDetectWeather() {
  return new Promise((resolve, reject) => {
    if (!navigator.geolocation) {
      reject(new Error('Geolocation not supported'));
      return;
    }

    navigator.geolocation.getCurrentPosition(
      async (position) => {
        const data = await getWeatherByCoords(
          position.coords.latitude,
          position.coords.longitude
        );
        resolve(data);
      },
      (error) => reject(new Error(`Geolocation: ${error.message}`)),
      { timeout: 10000 }
    );
  });
}

Storing the API Key Securely

For an extension, don't hardcode the API key in source. Use browser.storage.local and let users enter their own key:

async function getApiKey() {
  const { apiKey } = await browser.storage.local.get('apiKey');
  if (!apiKey) throw new Error('No API key configured');
  return apiKey;
}

async function setApiKey(key) {
  await browser.storage.local.set({ apiKey: key });
}

Or for a simpler approach with a bundled key, at least use an environment variable at build time:

// In your build process:
const OWM_KEY = process.env.OWM_API_KEY;

Rate Limiting

With 1M free requests/month, one user refreshing every 10 minutes for 30 days = 4,320 requests — well within limits. But if your extension has many users, consider:

const REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes
let lastFetch = 0;

async function fetchWeatherIfStale(city) {
  const now = Date.now();
  if (now - lastFetch < REFRESH_INTERVAL) {
    return getCachedWeather();
  }
  lastFetch = now;
  return getWeather(city);
}

The complete implementation is live in the Weather & Clock Dashboard Firefox extension.

firefox #javascript #webdev #api #browserextension

DE
Source

This article was originally published by DEV Community and written by Weather Clock Dash.

Read original article on DEV Community
Back to Discover

Reading List