Most weather APIs hide behind registration walls, throttle free accounts heavily, or charge immediately for anything useful. Open-Meteo is different: completely free, no API key, no account, open-source, and it returns real hourly/daily forecasts.
One URL, no auth:
https://api.open-meteo.com/v1/forecast?latitude=40.7128&longitude=-74.0060¤t=temperature_2m,wind_speed_10m
Try it right now:
curl "https://api.open-meteo.com/v1/forecast?latitude=40.7128&longitude=-74.0060¤t=temperature_2m,wind_speed_10m,relative_humidity_2m,weather_code"
Response:
{
"latitude": 40.710335,
"longitude": -73.99235,
"timezone": "GMT",
"current": {
"time": "2026-03-26T16:15",
"temperature_2m": 18.0,
"wind_speed_10m": 19.0,
"relative_humidity_2m": 50,
"weather_code": 0
},
"current_units": {
"temperature_2m": "°C",
"wind_speed_10m": "km/h",
"relative_humidity_2m": "%",
"weather_code": "wmo code"
}
}
No API key header. No rate limit response. No credit card.
How it works: latitude + longitude + variables
Every request needs two parameters: latitude and longitude. Everything else is optional — you specify which variables you want.
Current weather variables:
-
temperature_2m— air temperature at 2m height (°C or °F) -
wind_speed_10m— wind speed at 10m (km/h, mph, m/s, or knots) -
relative_humidity_2m— humidity (%) -
precipitation— precipitation in the last hour (mm) -
weather_code— WMO weather code (0=clear, 1-3=partly cloudy, 45=fog, 61-67=rain, 71-77=snow, etc.) -
apparent_temperature— feels-like temperature -
is_day— 1 if daytime, 0 if night
Hourly forecast variables (up to 16 days):
-
temperature_2m,precipitation_probability,wind_speed_10m -
uv_index,visibility,soil_temperature_0cm
Python: current weather for any city
import requests
def get_weather(lat: float, lon: float) -> dict:
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": lat,
"longitude": lon,
"current": "temperature_2m,apparent_temperature,wind_speed_10m,relative_humidity_2m,weather_code,precipitation",
"wind_speed_unit": "mph",
"temperature_unit": "fahrenheit"
}
response = requests.get(url, params=params)
response.raise_for_status()
return response.json()["current"]
# New York City
weather = get_weather(40.7128, -74.0060)
print(f"Temperature: {weather['temperature_2m']}°F (feels like {weather['apparent_temperature']}°F)")
print(f"Wind: {weather['wind_speed_10m']} mph")
print(f"Humidity: {weather['relative_humidity_2m']}%")
print(f"Precipitation: {weather['precipitation']} mm")
Output:
Temperature: 64.4°F (feels like 61.2°F)
Wind: 11.8 mph
Humidity: 50%
Precipitation: 0.0 mm
Get an hourly 3-day forecast
import requests
from datetime import datetime
def get_forecast(lat: float, lon: float, days: int = 3) -> list[dict]:
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": lat,
"longitude": lon,
"hourly": "temperature_2m,precipitation_probability,wind_speed_10m",
"forecast_days": days,
"temperature_unit": "fahrenheit"
}
data = requests.get(url, params=params).json()
hourly = data["hourly"]
# Zip into list of dicts
return [
{
"time": t,
"temp_f": temp,
"precip_pct": precip,
"wind_mph": wind
}
for t, temp, precip, wind in zip(
hourly["time"],
hourly["temperature_2m"],
hourly["precipitation_probability"],
hourly["wind_speed_10m"]
)
]
forecast = get_forecast(40.7128, -74.0060)
for hour in forecast[:6]: # First 6 hours
print(f"{hour['time']}: {hour['temp_f']}°F, {hour['precip_pct']}% rain chance, {hour['wind_mph']} mph")
Output:
2026-03-26T00:00: 47.2°F, 5% rain chance, 6.3 mph
2026-03-26T01:00: 45.8°F, 4% rain chance, 5.8 mph
2026-03-26T02:00: 44.5°F, 3% rain chance, 5.1 mph
2026-03-26T03:00: 43.7°F, 3% rain chance, 4.9 mph
2026-03-26T04:00: 42.9°F, 2% rain chance, 5.2 mph
2026-03-26T05:00: 42.4°F, 2% rain chance, 5.5 mph
Historical weather data (also free)
Open-Meteo has a separate endpoint for historical data, also free:
import requests
def get_historical(lat: float, lon: float, start: str, end: str) -> dict:
url = "https://archive-api.open-meteo.com/v1/archive"
params = {
"latitude": lat,
"longitude": lon,
"start_date": start,
"end_date": end,
"daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max"
}
return requests.get(url, params=params).json()
# Get last week's data for London
history = get_historical(51.5074, -0.1278, "2026-03-19", "2026-03-25")
daily = history["daily"]
for date, max_t, min_t, precip in zip(
daily["time"],
daily["temperature_2m_max"],
daily["temperature_2m_min"],
daily["precipitation_sum"]
):
print(f"{date}: {min_t}–{max_t}°C, {precip}mm rain")
The archive goes back to 1940 for most locations. No key. No account.
JavaScript: weather widget in the browser
async function getCurrentWeather(lat, lon) {
const params = new URLSearchParams({
latitude: lat,
longitude: lon,
current: 'temperature_2m,apparent_temperature,weather_code,wind_speed_10m',
temperature_unit: 'fahrenheit'
});
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?${params}`
);
const data = await response.json();
return data.current;
}
// Works directly from the browser — no CORS issues
getCurrentWeather(34.0522, -118.2437).then(weather => {
console.log(`LA: ${weather.temperature_2m}°F, code: ${weather.weather_code}`);
});
No CORS issues — Open-Meteo has Access-Control-Allow-Origin: *.
What the weather codes mean
The weather_code field uses WMO weather interpretation codes:
WMO_CODES = {
0: "Clear sky",
1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast",
45: "Fog", 48: "Icy fog",
51: "Light drizzle", 53: "Moderate drizzle", 55: "Heavy drizzle",
61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain",
71: "Slight snow", 73: "Moderate snow", 75: "Heavy snow",
80: "Slight showers", 81: "Moderate showers", 82: "Heavy showers",
95: "Thunderstorm",
}
def describe_weather(code: int) -> str:
return WMO_CODES.get(code, f"Unknown ({code})")
print(describe_weather(0)) # Clear sky
print(describe_weather(63)) # Moderate rain
Free vs. paid (commercial)
Open-Meteo is free for non-commercial use. The limits:
| Tier | Cost | Rate limit | Use case |
|---|---|---|---|
| Free | $0 | ~10,000 req/day | Personal projects, apps, research |
| Commercial | From $29/mo | Higher limits + SLA | Production apps |
For most developer projects — dashboards, personal apps, learning — the free tier is more than enough.
What it doesn't do
- No severe weather alerts (use NWS API for US alerts)
- No minute-by-minute nowcasting (free tier updates hourly)
- No air quality data (separate API at air-quality-api.open-meteo.com, also free)
Need weather data on autopilot? My Weather Forecast Scraper on Apify collects temperature, rain & wind forecasts on a schedule — no code. Email spinov001@gmail.com for custom pipelines.
More free API articles:
Top comments (0)