Using the Open-Meteo API in Python, JavaScript, and curl
Languages: curl, JavaScript, Python · Estimated time: ~5 minutes · Open-Meteo live status ↗
Open-Meteo requires no API key and gives you 10,000 calls/day. This tutorial shows you how to fetch a forecast, parse the response, and handle the WMO weather codes.
Open-Meteo is the cleanest free weather API available: no key, 10,000 calls/day, JSON output, global coverage using ECMWF and NOAA models. You can make your first request right now without signing up for anything.
The live status of the Open-Meteo API is at /open-meteo — we check it every hour. As of this writing it has exceptional uptime.
A Basic Forecast Request with curl
This request fetches current temperature and weather code for Berlin (lat=52.52, lon=13.41). No headers needed.
curl "https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t=temperature_2m,weathercode,windspeed_10m,precipitation"
# Response (abbreviated):
# {
# "latitude": 52.52,
# "longitude": 13.41,
# "timezone": "GMT",
# "current": {
# "time": "2026-05-15T12:00",
# "temperature_2m": 17.3,
# "weathercode": 3,
# "windspeed_10m": 12.4,
# "precipitation": 0.0
# }
# } JavaScript: Current + Hourly Forecast
The `hourly` key returns parallel arrays indexed by hour. Use `timezone=auto` to let Open-Meteo infer the timezone from the coordinates.
const LAT = 48.8566; // Paris
const LON = 2.3522;
const params = new URLSearchParams({
latitude: LAT,
longitude: LON,
current: "temperature_2m,apparent_temperature,weathercode,precipitation",
hourly: "temperature_2m,precipitation_probability,weathercode",
forecast_days: "3",
timezone: "auto"
});
const res = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`);
const data = await res.json();
// Current conditions
const { temperature_2m, apparent_temperature, weathercode } = data.current;
console.log(`Temperature: ${temperature_2m}°C (feels like ${apparent_temperature}°C)`);
console.log(`WMO code: ${weathercode}`);
// Hourly: zip the parallel arrays
const hourly = data.hourly.time.map((time, i) => ({
time,
temp: data.hourly.temperature_2m[i],
precipChance: data.hourly.precipitation_probability[i],
code: data.hourly.weathercode[i]
}));
// Print next 6 hours
hourly.slice(0, 6).forEach(h => {
console.log(`${h.time}: ${h.temp}°C, ${h.precipChance}% rain chance`);
}); Python: Fetching and Parsing with requests
import requests
def get_weather(lat: float, lon: float) -> dict:
params = {
"latitude": lat,
"longitude": lon,
"current": "temperature_2m,apparent_temperature,weathercode,precipitation",
"hourly": "temperature_2m,precipitation_probability",
"forecast_days": 3,
"timezone": "auto"
}
resp = requests.get("https://api.open-meteo.com/v1/forecast", params=params, timeout=10)
resp.raise_for_status()
return resp.json()
# London
data = get_weather(51.5074, -0.1278)
current = data["current"]
print(f"Temperature: {current['temperature_2m']}°C")
print(f"Feels like: {current['apparent_temperature']}°C")
# Hourly data as list of dicts
hourly_times = data["hourly"]["time"]
hourly_temps = data["hourly"]["temperature_2m"]
hourly_precip = data["hourly"]["precipitation_probability"]
for i in range(6): # next 6 hours
print(f"{hourly_times[i]}: {hourly_temps[i]}°C, {hourly_precip[i]}% rain") WMO Weather Codes
The `weathercode` field follows the WMO (World Meteorological Organisation) standard. Key codes to handle in your UI:
- 0 — Clear sky
- 1, 2, 3 — Mainly clear, partly cloudy, overcast
- 45, 48 — Fog and depositing rime fog
- 51, 53, 55 — Drizzle: light, moderate, dense
- 61, 63, 65 — Rain: slight, moderate, heavy
- 71, 73, 75 — Snow: slight, moderate, heavy
- 80, 81, 82 — Rain showers: slight, moderate, violent
- 95 — Thunderstorm
- 96, 99 — Thunderstorm with slight/heavy hail
Common Gotchas
- Coordinates must be decimal degrees (not DMS). lat=51.5, not lat=51°30'N.
- The hourly arrays start from midnight of the current day, not the current hour. Slice by finding the current hour's index.
- timezone=auto is usually what you want. Without it, times are in UTC and you need to convert.
- forecast_days defaults to 7. Reduce it if you only need today — smaller responses are faster.
- The API returns HTTP 200 even for invalid parameter combinations — check for an 'error' key in the response.