GuidesWEATHER APIS

Free weather APIs tested from the browser

We tested 11 free weather APIs from a browser on 26 October 2026: 10 send CORS headers and work with fetch() directly, only 7Timer needs a proxy, and 6 need no API key. Open-Meteo is the default pick: no key, current weather plus a 7-day forecast in one request, free up to 10,000 calls a day for non-commercial use. For US forecasts, api.weather.gov is free for any purpose.

Every weather API we tested

We ran each API on 26 October 2026 with curl and an Origin: https://example.com header from a German datacenter IP and a US residential IP, then in Chromium 147.0.7727.15 from http://127.0.0.1:8001 and https://example.com. To check another endpoint, use the CORS header checker or our 70-API list of free public APIs.

Free weather APIs tested from a browser with fetch(), checked October 2026
APIAPI keyCORS from the browserFree limitCoverage and notes
Open-MeteoNoneYes, ACAO *10,000 calls/day, 5,000/hour, 600/minute, non-commercial onlyGlobal forecast; CC BY 4.0 attribution
NWS api.weather.govNoneYes, ACAO *Not published; a retry usually works within 5 secondsUS only; points lookup, then forecast URL
MET Norway Locationforecast 2.0NoneYes, ACAO *Over 20 requests/second per app counts as heavy useGlobal; identify your app; CC BY 4.0
Bright SkyNoneYes, ACAO *Free for all purposes, no quota publishedGerman DWD data, sparser outside Germany
wttr.in (format=j1)NoneYes, ACAO *No quota publishedJSON labelled text/plain, about 40 KB per city
7TimerNoneNo, needs a proxyNo quota publishedGFS model, updated 4 times a day
OpenWeatherMapFree key (appid)Yes, ACAO *60 calls/minute, 1,000,000 calls/monthOne Call 3.0 is a separate subscription
WeatherAPI.comFree keyYes, ACAO *100,000 calls/monthKey goes in the URL as key=
Tomorrow.ioFree keyYes, echoes your OriginPer second, hour and day; shown in X-RateLimit headersFree plan: 5-day forecast, 1 location
Visual CrossingFree keyYes, ACAO *1,000 records/day, 30,000 records/monthCommercial use allowed on the free plan
Pirate WeatherFree key in the URL pathYes on a plain GET; preflight gets 40310,000 calls/monthDark Sky style API

Open-Meteo: the default with no key

Open-Meteo (https://api.open-meteo.com/v1/forecast) takes no API key, sends Access-Control-Allow-Origin: *, and returns current conditions plus a 7-day forecast in one call when you pass current, daily, forecast_days=7 and timezone=auto. Call it directly from fetch().

Its terms allow non-commercial use only, with less than 10,000 calls per day, 5,000 per hour and 600 per minute, counted per IP address and per application. Commercial products need a paid plan. The data is licensed under CC BY 4.0, so credit Open-Meteo in your app.

The forecast takes latitude and longitude, not place names. Open-Meteo also runs a geocoding API and an air-quality API, both with Access-Control-Allow-Origin: *; our free geocoding API guide shows how to turn a city name into coordinates.

Open-Meteo: current weather and a 7-day forecast, no key
const params = new URLSearchParams({
  latitude: '52.37',
  longitude: '4.89',
  current: 'temperature_2m,wind_speed_10m',
  daily: 'temperature_2m_max,temperature_2m_min,precipitation_sum',
  timezone: 'auto',
  forecast_days: '7',
});

const response = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`);
const body = await response.json().catch(() => null);

if (!response.ok) {
  throw new Error(`Open-Meteo failed: ${body?.reason ?? response.status}`);
}

console.log('Now:', body.current.temperature_2m, body.current_units.temperature_2m);
console.table(
  body.daily.time.map((day, i) => ({
    day,
    max: body.daily.temperature_2m_max[i],
    min: body.daily.temperature_2m_min[i],
    rainMm: body.daily.precipitation_sum[i],
  })),
);

US forecasts from api.weather.gov

The National Weather Service API at https://api.weather.gov covers the US only, needs no key, and sends Access-Control-Allow-Origin: * with application/geo+json responses. A forecast takes two requests: GET /points/{lat},{lon} returns properties.forecast, a gridpoint URL like https://api.weather.gov/gridpoints/TOP/32,81/forecast, and a GET on that URL returns 14 periods, day and night for 7 days. Outside the US, /points answers 404 with "Unable to provide data for requested point".

The docs ask for a User-Agent that identifies your app, such as (myweatherapp.com, contact@myweatherapp.com), and curl with an empty User-Agent got 403 Access Denied. Chrome does not let fetch() replace that header: Chromium 147 silently dropped our custom User-Agent, sent its own, and still got 200.

Call it directly, not through a proxy. The rate limit is not public, and the docs say proxies are more likely to reach it than requests straight from clients. When you hit it, retry after the limit clears, typically within 5 seconds.

api.weather.gov: points lookup, then the forecast URL it returns
async function getGeoJson(url) {
  const response = await fetch(url, { headers: { Accept: 'application/geo+json' } });
  const body = await response.json().catch(() => null);

  if (!response.ok) {
    throw new Error(`${url} failed: ${body?.detail ?? response.status}`);
  }
  return body;
}

const point = await getGeoJson('https://api.weather.gov/points/38.8977,-77.0365');
const forecast = await getGeoJson(point.properties.forecast);

console.table(
  forecast.properties.periods.map(({ name, temperature, temperatureUnit, shortForecast }) => ({
    name,
    temperature: `${temperature} ${temperatureUnit}`,
    shortForecast,
  })),
);

MET Norway, Bright Sky and wttr.in

  • MET Norway Locationforecast 2.0 (api.met.no/weatherapi/locationforecast/2.0/compact): no key, Access-Control-Allow-Origin: *, 39,836 bytes for one location, CC BY 4.0. The terms ask for an identifying User-Agent, and accept the Origin or Referer that browsers send when cross-origin JavaScript can't set one. Don't add a custom User-Agent header: the preflight for it returned 204 without CORS headers, so the browser blocks the call. More than 20 requests per second for your whole app counts as heavy use; cache responses and use If-Modified-Since.
  • Bright Sky (api.brightsky.dev): no key, Access-Control-Allow-Origin: *, German weather service (DWD) data under the DWD terms of use. The public instance is free for all purposes with no published quota; /current_weather returned 1,623 bytes.
  • wttr.in (wttr.in/Amsterdam?format=j1): no key, Access-Control-Allow-Origin: *, no published quota. It returned 39,553 bytes of JSON labelled Content-Type: text/plain, which response.json() still parses.

7Timer needs a proxy

7Timer (https://www.7timer.info/bin/api.pl) runs on the NOAA GFS model, updates 4 times a day and needs no key; the civil product with output=json gives 3-hourly steps. It sends no CORS headers, and Chromium 147 blocked it with: Access to fetch at '...' from origin 'http://127.0.0.1:8001' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Prefix the URL with https://proxy.cors.dev/ and the same request returns 200 with 15,177 bytes of JSON. The free CORS proxy needs no key or signup and handles GET and HEAD for text responses up to 1 MiB, with a 10-second deadline and 600 requests per minute per client IP.

7Timer through the cors.dev proxy (it sends no CORS headers)
const proxy = 'https://proxy.cors.dev/';
const url = 'https://www.7timer.info/bin/api.pl?lon=4.89&lat=52.37&product=civillight&output=json';

const response = await fetch(proxy + url, { credentials: 'omit' });

if (!response.ok) {
  throw new Error(`7Timer via proxy failed: ${response.headers.get('X-Cors-Error') ?? response.status}`);
}

const body = await response.json();

console.table(
  body.dataseries.map(({ date, weather, temp2m }) => ({
    date,
    weather,
    max: temp2m.max,
    min: temp2m.min,
  })),
);

Weather APIs with a free key

  • OpenWeatherMap: key in appid, Access-Control-Allow-Origin: * even on a 401. The free plan gives 60 calls per minute and 1,000,000 per month for current weather, the 3-hour 5-day forecast, air pollution, maps and geocoding. One Call API 3.0 is a separate subscription: 1,000 free calls per day, then pay per call, and you fill out a billing form to subscribe.
  • WeatherAPI.com: key in key=, Access-Control-Allow-Origin: *, 100,000 calls per month free.
  • Tomorrow.io: key in apikey=, echoes your Origin. The free plan covers a 5-day forecast, 24 hours of history, 1 location and 1 alert; its per-second, hourly and daily limits show up only in X-RateLimit response headers.
  • Visual Crossing: key in key=, Access-Control-Allow-Origin: *, 1,000 records per day and 30,000 per month, commercial use allowed.
  • Pirate Weather: a Dark Sky style API with the key in the URL path and 10,000 free calls per month. A plain GET gets Access-Control-Allow-Origin: *, but its preflight returned 403 without CORS headers, so add no custom headers.

A key in frontend code is visible to every visitor, who can copy it and spend your quota. All five already send CORS headers, so call them directly; the cors.dev free tier refuses URLs with key=, apikey= and similar parameters (HTTP 400, X-Cors-Error: credential_in_url).

Good questions.

Which free weather API works without an API key?

Six of the 11 we tested: Open-Meteo, api.weather.gov, MET Norway, Bright Sky, wttr.in and 7Timer. All but 7Timer send Access-Control-Allow-Origin: *, and Open-Meteo is the easiest default at up to 10,000 calls per day for non-commercial apps.

Is Open-Meteo free for commercial use?

No. The free API (10,000 calls per day, 5,000 per hour, 600 per minute) is non-commercial only. For free commercial use without a key, api.weather.gov (US only) is free for any purpose, Bright Sky is free for all purposes under the DWD terms, and MET Norway publishes under CC BY 4.0, which allows commercial use with credit.

Do I need to set a User-Agent for api.weather.gov in the browser?

No. Chrome does not let fetch() override User-Agent: Chromium 147 dropped our custom value, sent its own, and got 200. Send an identifying User-Agent when you call the API from a server.

Is the OpenWeatherMap One Call API free?

One Call API 3.0 includes 1,000 free calls per day, but it is a separate subscription that asks for billing details and charges per call above that. The standard free plan gives 1,000,000 calls per month at up to 60 per minute without One Call.

How do I turn a city name into coordinates for a weather API?

Open-Meteo's geocoding API turns a city name into latitude and longitude with no key and sends Access-Control-Allow-Origin: *. Our free geocoding API guide compares 11 geocoders with code.

Can I show local weather without asking for the user's location?

Yes, approximately: look up the visitor's IP address with a free IP geolocation API and pass the coordinates to Open-Meteo or api.weather.gov. The exact position needs the browser's geolocation permission.