FreeAPI.watch

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&current=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.

Frequently Asked Questions

Does Open-Meteo have a rate limit?

10,000 calls per day on the non-commercial free tier. There is no per-minute rate limit, but excessive bursting may be throttled. For commercial use, their paid plans start at a few hundred dollars per month and remove the non-commercial restriction.

Can I get historical weather data?

Yes. Use the /v1/archive endpoint with a start_date and end_date parameter. Historical data is available from 1940 onwards for most locations. This uses the same no-key, no-credit-card access.

What does 'non-commercial' mean?

Open-Meteo's free tier is for non-commercial use. If you're building a product you sell or that generates revenue, you should use their commercial plan. Personal projects, open-source apps, and internal tools are typically fine on the free tier — but check their terms at open-meteo.com/en/terms.

← All tutorials  ·  Check Open-Meteo live status →