You want the temperature in your app. You do not want to create an account, wait for an email, or paste a key you will later leak. So you search for a weather API with no key.
The problem: many "no key" lists were written once and never rechecked. One roundup we read says every API on its list was "tested and confirmed live", but shows no test results. We did the testing for real. On 21 October 2026 we called six keyless weather APIs from a script, recorded the status codes, timings and errors, and checked each provider's own terms. We read 15 sources in all.
"No key" does not mean "no rules"#
Here is what each provider's own pages say. We link every source at the end.
| API | Coverage | Free limit | Commercial use | What it asks of you |
|---|---|---|---|---|
| Open-Meteo | Worldwide | 600 per minute, 5,000 per hour, 10,000 per day, 300,000 per month | No. A paid plan is needed | Data is CC BY 4.0. Limits are per IP address and app |
| NWS (api.weather.gov) | US only | "Reasonable" limits, number not published | Not addressed on the page we read | Send a User-Agent. NWS says an API key will replace this in future |
| MET Norway | Worldwide | 20 requests per second per application | Not addressed directly | Identify your app, cache, credit under CC BY 4.0, truncate coordinates to 4 decimals |
| wttr.in | Worldwide | No limit documented | Not stated | Use a "reasonable update interval". A mirror, wttr.is, exists |
| Bright Sky | Germany (DWD data) | No limit stated | Not stated | Community project with a free public instance |
| 7Timer | Worldwide | No limit documented | No. Non-commercial only | Asks developers to notify the owner. Plain http only |
Two rules matter most. First, Open-Meteo's free API is for non-commercial use. Its terms count sites or apps with ads or subscriptions as commercial. Second, NWS and MET Norway both want you to identify yourself. Skipping that is the most common reason a "no key" call fails.
What we found when we tested them#
We ran one script that called each API once and timed the reply. This is a single run from one laptop, not a benchmark, so read the timings as "all fast enough", not as a ranking.
| Call | Result | Time | Size |
|---|---|---|---|
| Open-Meteo current weather, Berlin | 200 OK, 12.6 C, wind 7.3 km/h | 703 ms | 385 B |
| Open-Meteo 16-day daily forecast | 200 OK, 16 days | 703 ms | 687 B |
| Open-Meteo geocoding, "Berlin" | 200 OK, 52.52437, 13.41053 | 736 ms | 423 B |
NWS /points with User-Agent | 200 OK, office TOP, grid 32,81 | 577 ms | 3.9 KB |
| NWS forecast (second step) | 200 OK, 14 periods | 573 ms | 13.4 KB |
NWS /points with no User-Agent | 403 Forbidden | 185 ms | |
NWS /points for Berlin | 404 Not Found | 488 ms | |
| MET Norway forecast, Oslo | 200 OK, 87 time steps | 718 ms | 37 KB |
wttr.in format=j1, London | 200 OK, 3 forecast days | 932 ms | 39.7 KB |
| Bright Sky current weather, Berlin | 200 OK | 635 ms | 1.3 KB |
| 7Timer civil forecast | 200 OK, 64 time points | 527 ms | 15 KB |
Notice the sizes. Open-Meteo returns only the fields you ask for, so its replies are tiny. MET Norway and wttr.in send tens of kilobytes whether you want it or not, which matters on slow mobile connections.
Three surprises worth knowing#
- MET Norway rejected a placeholder User-Agent. A request with the contact
[email protected]returned 403. The same request with a realistic contact returned 200. MET Norway's terms say fake or random identifiers count as abuse, so put a real contact in. (In our test, requests with no User-Agent at all, or curl's default, still returned 200, but the terms say to include one, so do not rely on that.) - Open-Meteo quietly defaults to GMT. Its docs say you must set
timezonewhen you ask for daily values. In our test, leaving it out still returned 200, but the reply said"timezone":"GMT", so a "day" would not match the local day. Always sendtimezone=auto. - MET Norway let a 5-decimal coordinate through. Its terms say to truncate coordinates to 4 decimals and say longer ones can return 403 for new products. Our forecast call with 5 decimals still returned 200, but follow the rule anyway.
Open-Meteo: the best default#
Open-Meteo is the strongest pick for most projects. The forecast endpoint is https://api.open-meteo.com/v1/forecast. You pass latitude and longitude, then list the values you want. Its docs say forecast_days accepts 0 to 16, and current values come from 15-minute model data.
Here is a call we ran, with the real response from 21 October:
curl -s "https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t=temperature_2m,wind_speed_10m&timezone=auto"In Python, using only the standard library:
import json
import urllib.request
url = (
"https://api.open-meteo.com/v1/forecast"
"?latitude=52.52&longitude=13.41"
"¤t=temperature_2m,wind_speed_10m&timezone=auto"
)
with urllib.request.urlopen(url, timeout=10) as response:
data = json.load(response)
print(data["current"]["temperature_2m"], data["current"]["wind_speed_10m"])That printed 12.6 7.3 when we ran it. Remember the limits: 10,000 calls a day and 600 a minute on the free tier, applied per IP address and per application. Open-Meteo's terms warn that shared hosting can hit the limits sooner and that misuse can be blocked without notice. Cache results: weather does not change every second.
Need a city name instead of coordinates? Open-Meteo has a geocoding endpoint:
curl -s "https://geocoding-api.open-meteo.com/v1/search?name=Berlin&count=1"US National Weather Service: official data, two steps#
The NWS API at api.weather.gov serves official US forecasts and alerts. There is no key, but NWS says "a User Agent is required to identify your application" and that it plans to replace this with an API key later. Putting contact details in the User-Agent lets NWS reach you if something goes wrong.
It works in two steps. First you ask which forecast grid covers your coordinates. Then you request that grid's forecast:
import json
import urllib.request
HEADERS = {"User-Agent": "my-weather-app/1.0 (contact: your-real-email)"}
def get(url):
request = urllib.request.Request(url, headers=HEADERS)
with urllib.request.urlopen(request, timeout=10) as response:
return json.load(response)
point = get("https://api.weather.gov/points/39.7456,-97.0892")
forecast = get(point["properties"]["forecast"])
first = forecast["properties"]["periods"][0]
print(first["name"], first["temperature"], first["temperatureUnit"])When we ran the equivalent calls, step one returned office TOP, grid 32,81, and step two returned 14 forecast periods, starting with "Overnight 54 F". Without a User-Agent the first call returned 403. For a place outside the US, such as Berlin, it returned 404.
NWS says grid mappings can be cached but should be rechecked from time to time. It does not publish an exact rate limit, only "reasonable rate limits", and says a request over the limit may be retried after the limit clears, typically within 5 seconds. It also says proxies are more likely to hit the limit than direct clients.
MET Norway: a good second source#
MET Norway's Locationforecast covers the whole world and is a solid backup. The compact endpoint looks like this:
import json
import urllib.request
url = "https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=59.9100&lon=10.7500"
request = urllib.request.Request(
url, headers={"User-Agent": "my-weather-app/1.0 (contact: your-real-email)"}
)
with urllib.request.urlopen(request, timeout=10) as response:
data = json.load(response)
now = data["properties"]["timeseries"][0]["data"]["instant"]["details"]
print(now["air_temperature"])MET Norway's terms ask for good manners as well as the header. Allow up to 20 requests per second per application, cache responses, and do not re-request before the time in the Expires header. Send If-Modified-Since with the previous Last-Modified value. The data is open under CC BY 4.0, which means you must give credit, link the license and say if you changed the data. Do not imply endorsement by Yr, NRK or the Norwegian Meteorological Institute.
wttr.in, Bright Sky and 7Timer: niche picks#
wttr.in is built for the terminal. curl wttr.in/London?format=3 gives a one-line answer, and format=j1 gives JSON. Its README says to use a reasonable update interval and lists wttr.is as a drop-in mirror for scripts. We got a 3-day forecast, but the reply was nearly 40 KB, so it is a poor fit for an app. It is great for a status bar or a quick check.
Bright Sky is a community project that serves German national weather service (DWD) observations and forecasts as JSON, through a free public instance. Use it only if your users are in Germany. In our test, https://api.brightsky.dev/current_weather?lat=52.52&lon=13.41 returned a reading from a nearby station in about 635 ms.
7Timer offers civil, astronomy and meteorological forecasts and needs no key, but its data is free only for non-commercial use, and its API is served over plain http. That means anyone on the network can read or alter the traffic, so avoid it for anything sensitive. It suits hobby projects and stargazing tools.
curl -s "https://wttr.in/London?format=3"
curl -s "https://api.brightsky.dev/current_weather?lat=52.52&lon=13.41"
curl -s "http://www.7timer.info/bin/api.pl?lon=13.41&lat=52.52&product=civil&output=json"A fallback client that survives an outage#
Never depend on one free service. This small script tries Open-Meteo first and falls back to MET Norway. It sets a timeout, truncates coordinates for MET Norway, and sends a User-Agent:
import json
import urllib.request
USER_AGENT = "my-weather-app/1.0 (contact: your-real-email)"
def fetch_json(url, timeout=10):
request = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(request, timeout=timeout) as response:
return json.load(response)
def from_open_meteo(lat, lon, base="https://api.open-meteo.com"):
url = f"{base}/v1/forecast?latitude={lat}&longitude={lon}¤t=temperature_2m"
return fetch_json(url)["current"]["temperature_2m"]
def from_met_norway(lat, lon):
# MET Norway asks for coordinates truncated to 4 decimals.
url = f"https://api.met.no/weatherapi/locationforecast/2.0/compact?lat={lat:.4f}&lon={lon:.4f}"
first = fetch_json(url)["properties"]["timeseries"][0]
return first["data"]["instant"]["details"]["air_temperature"]
def current_temperature_c(lat, lon, open_meteo_base="https://api.open-meteo.com"):
"""Try the primary source, then fall back. Returns (temperature, source)."""
for name, getter in (
("Open-Meteo", lambda: from_open_meteo(lat, lon, open_meteo_base)),
("MET Norway", lambda: from_met_norway(lat, lon)),
):
try:
return getter(), name
except Exception as err:
print(f"{name} failed: {type(err).__name__}")
raise RuntimeError("All weather sources failed")
print(current_temperature_c(52.52, 13.41))
# Force the first source to fail to prove the fallback works.
print(current_temperature_c(52.52, 13.41, open_meteo_base="https://api.open-meteo.invalid"))We ran this with a realistic contact in the User-Agent. The first call returned (12.6, 'Open-Meteo'). With Open-Meteo forced to fail, the second printed Open-Meteo failed: URLError and then returned (12.3, 'MET Norway'). Replace the contact with your own real address, or MET Norway may answer with a 403, as it did when our first attempt used a placeholder example.com address.
For a production app, add a cache so you call each API at most every few minutes per location. Our guide to API design principles explains why timeouts and clear error handling matter, and what a REST API is covers the basics if the status codes in this post are new to you.
Which one should you use?#
| Your situation | Best pick | Why |
|---|---|---|
| Hobby project, anywhere in the world | Open-Meteo | No key, tiny replies, 16-day forecast |
| US app that needs official forecasts or alerts | NWS | Official source, but US only and needs a User-Agent |
| A second source for failover | MET Norway | Worldwide, generous 20 per second, needs credit and a real contact |
| A terminal status bar or script | wttr.in | One-line output in one command |
| Users in Germany | Bright Sky | DWD observations and forecasts |
| Astronomy or stargazing | 7Timer | Dedicated astro product, non-commercial only |
| A paid or ad-supported product | Read each license first | Open-Meteo's free tier is non-commercial |
When you do need a key#
Keyless APIs cover a lot, but some jobs need a key: city-name search built in, minute-level forecasts, years of history, or a commercial licence on a free plan. These are the official free plans as we read them on 21 October 2026:
| Provider | Free plan (from its own page) | Notes |
|---|---|---|
| OpenWeatherMap | 60 calls per minute and 1,000,000 per month on its Weather APIs. One Call is listed with the first 1,000 calls per day free | The pricing page did not say whether a card is needed |
| WeatherAPI.com | 100K calls per month, 3-day forecast, 1 day of history | Key and card requirements are not stated on the page |
| Visual Crossing | 30,000 records per month, with a separate daily limit it does not state | Commercial use allowed; attribution required on Free |
Roundups disagree with the official pages. One guide we read lists WeatherAPI.com at 1,000,000 calls a month, but WeatherAPI.com's own pricing page says 100K. Another lists Visual Crossing at 1,000 requests a day, while its own page lists 30,000 records a month. The first of those guides also prints an Open-Meteo example URL with a mangled ¤t_weather=true, so the copied link would not work. Trust the provider's pricing page, and check the date.
Also be careful with vendor pages. Weatherstack's "no key" article says its API works without a key, yet the same page links to sign-up, and it gives no numbers. Treat vendor marketing as marketing. For more on spotting stale advice, see our post on outdated API tutorials.
Common mistakes#
- Leaving out the User-Agent. NWS returns 403 without it, and MET Norway's terms require it.
- Using a fake contact. MET Norway returned 403 for an
example.comaddress. - Forgetting
timezone=autoon Open-Meteo daily data. You get GMT days. - Shipping Open-Meteo's free API in an ad-supported app. That is commercial use and needs a paid plan.
- Calling an API on every page view. Cache per location for a few minutes.
- Having one source only. Free services can be slow, blocked or down.
- Forgetting credit. Open-Meteo data and MET Norway data are both CC BY 4.0.
- Using NWS for a non-US location. It returns 404.
Checklist before you launch#
- Commercial or not? Decided, and the license read.
- Real contact in every User-Agent.
-
timezone=autoon daily forecasts. - Timeouts on every call, and a second source behind the first.
- A cache, keyed by location, with a short expiry.
- Attribution shown where the license asks for it.
- Coordinates truncated to 4 decimals for MET Norway.
Key takeaways#
- Six weather APIs answered with no key in our 21 October 2026 tests: Open-Meteo, NWS, MET Norway, wttr.in, Bright Sky and 7Timer.
- Open-Meteo is the best default, but its free tier is for non-commercial use only.
- NWS is US only and returned 403 without a User-Agent and 404 for Berlin.
- MET Norway returned 403 for a placeholder
example.comcontact, so use a real one. - Always add a timeout, a cache and a fallback source.
- Trust each provider's own pricing and terms pages over roundups, which disagreed with them.
Sources we read#
We read these 15 sources and ran the tests on 21 October 2026. Limits and terms change, so check the official pages before you launch.
Official provider pages: Open-Meteo pricing, Open-Meteo forecast docs, Open-Meteo terms, NWS API documentation, MET Norway terms of service, wttr.in on GitHub, Bright Sky on GitHub, 7Timer documentation, OpenWeatherMap pricing, WeatherAPI.com pricing, Visual Crossing plans.
Third-party guides: PublicAPIs.io: no-key weather APIs, Geekflare: 10 best weather APIs, APIBouncer: best free weather APIs, Weatherstack: free weather API, no key.
We also used Google Trends (United States, 12 months, checked 21 October 2026) to choose this topic. "free weather api" averaged 46 on Google's 0 to 100 scale and "open-meteo" 33, ahead of "weather api key" (29) and "openweathermap" (16). Interest in all of these dips over the summer and climbs from November.