free-weather-data
Free Weather Data API — Open-Meteo & wttr.in
Get current weather, forecasts, and historical weather data using free APIs. No API keys required. Privacy-respecting alternative to paid weather APIs ($5-100/month).
Why This Replaces Paid Weather APIs
💰 Cost savings:
- ✅ 100% free — no API keys, no rate limits
- ✅ Comprehensive data — current, forecast, historical weather
- ✅ Open source — Open-Meteo is fully open-source
- ✅ Privacy-first — no tracking or data collection
Perfect for AI agents that need:
- Weather conditions for location-based decisions
- Forecast data for planning and scheduling
- Historical weather data for analysis
- Weather alerts and monitoring
Quick comparison
| Service | Cost | Rate limit | API key required | Privacy |
|---|---|---|---|---|
| OpenWeatherMap | $0-180/month | 60 calls/min free | ✅ Yes | ❌ Tracked |
| WeatherAPI | $0-70/month | 1M calls/month free | ✅ Yes | ❌ Tracked |
| Open-Meteo | Free | 10k req/day | ❌ No | ✅ Private |
| wttr.in | Free | Unlimited | ❌ No | ✅ Private |
Skills
get_current_weather_open_meteo
Get current weather using Open-Meteo (most accurate and comprehensive).
# Current weather by coordinates
LAT=40.7128
LON=-74.0060
curl -s "https://api.open-meteo.com/v1/forecast?latitude=${LAT}&longitude=${LON}¤t=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m&temperature_unit=fahrenheit" \
| jq '{
temperature: .current.temperature_2m,
feels_like: .current.apparent_temperature,
humidity: .current.relative_humidity_2m,
wind_speed: .current.wind_speed_10m,
precipitation: .current.precipitation,
weather_code: .current.weather_code
}'
# With timezone
curl -s "https://api.open-meteo.com/v1/forecast?latitude=${LAT}&longitude=${LON}¤t=temperature_2m,weather_code&timezone=America/New_York" \
| jq '{temperature: .current.temperature_2m, time: .current.time}'
Node.js:
async function getCurrentWeather(lat, lon, unit = 'fahrenheit') {
const params = new URLSearchParams({
latitude: lat.toString(),
longitude: lon.toString(),
current: 'temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m,wind_direction_10m',
temperature_unit: unit
});
const res = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`);
if (!res.ok) {
throw new Error(`Weather API failed: ${res.status}`);
}
const data = await res.json();
return {
temperature: data.current.temperature_2m,
feelsLike: data.current.apparent_temperature,
humidity: data.current.relative_humidity_2m,
windSpeed: data.current.wind_speed_10m,
windDirection: data.current.wind_direction_10m,
precipitation: data.current.precipitation,
weatherCode: data.current.weather_code,
time: data.current.time,
unit: data.current_units.temperature_2m
};
}
// Usage
// getCurrentWeather(40.7128, -74.0060, 'fahrenheit')
// .then(weather => {
// console.log(`Temperature: ${weather.temperature}°${weather.unit}`);
// console.log(`Feels like: ${weather.feelsLike}°${weather.unit}`);
// console.log(`Humidity: ${weather.humidity}%`);
// console.log(`Wind: ${weather.windSpeed} mph`);
// });
get_weather_forecast
Get weather forecast for the next 7-16 days.
# 7-day forecast
LAT=37.7749
LON=-122.4194
curl -s "https://api.open-meteo.com/v1/forecast?latitude=${LAT}&longitude=${LON}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,weather_code&temperature_unit=fahrenheit&timezone=America/Los_Angeles" \
| jq '{
dates: .daily.time,
max_temp: .daily.temperature_2m_max,
min_temp: .daily.temperature_2m_min,
precipitation: .daily.precipitation_sum
}'
# Hourly forecast for next 24 hours
curl -s "https://api.open-meteo.com/v1/forecast?latitude=${LAT}&longitude=${LON}&hourly=temperature_2m,precipitation,weather_code&forecast_days=1" \
| jq '{hourly: [.hourly.time, .hourly.temperature_2m, .hourly.precipitation] | transpose | map({time: .[0], temp: .[1], precip: .[2]})}'
Node.js:
async function getWeatherForecast(lat, lon, days = 7, unit = 'fahrenheit') {
const params = new URLSearchParams({
latitude: lat.toString(),
longitude: lon.toString(),
daily: 'temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_probability_max,weather_code,wind_speed_10m_max',
temperature_unit: unit,
forecast_days: days.toString()
});
const res = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`);
if (!res.ok) {
throw new Error(`Forecast API failed: ${res.status}`);
}
const data = await res.json();
return data.daily.time.map((date, i) => ({
date,
maxTemp: data.daily.temperature_2m_max[i],
minTemp: data.daily.temperature_2m_min[i],
precipitation: data.daily.precipitation_sum[i],
precipitationProbability: data.daily.precipitation_probability_max[i],
maxWindSpeed: data.daily.wind_speed_10m_max[i],
weatherCode: data.daily.weather_code[i]
}));
}
// Usage
// getWeatherForecast(37.7749, -122.4194, 7, 'fahrenheit')
// .then(forecast => {
// forecast.forEach(day => {
// console.log(`${day.date}: ${day.minTemp}°-${day.maxTemp}°, ${day.precipitationProbability}% rain`);
// });
// });
get_weather_wttr_simple
Get simple weather using wttr.in (human-readable, very fast).
# Get weather by city name (plain text)
curl -s "https://wttr.in/London?format=3"
# Output: London: ☀️ +22°C
# Get detailed weather report
curl -s "https://wttr.in/Paris"
# JSON format
curl -s "https://wttr.in/Tokyo?format=j1" | jq '.current_condition[0] | {
temp_f: .temp_F,
humidity: .humidity,
description: .weatherDesc[0].value,
wind_mph: .windspeedMiles
}'
# Custom format (temperature and condition)
curl -s "https://wttr.in/NewYork?format=%C+%t"
# Output: Clear +75°F
# Get weather by coordinates
curl -s "https://wttr.in/40.7128,-74.0060?format=%l:+%C+%t"
Node.js:
async function getWeatherWttr(location) {
// location can be city name or "lat,lon"
const res = await fetch(`https://wttr.in/${encodeURIComponent(location)}?format=j1`);
if (!res.ok) {
throw new Error(`Weather API failed: ${res.status}`);
}
const data = await res.json();
const current = data.current_condition[0];
return {
location: data.nearest_area[0]?.areaName[0]?.value || location,
tempF: parseInt(current.temp_F),
tempC: parseInt(current.temp_C),
feelsLikeF: parseInt(current.FeelsLikeF),
feelsLikeC: parseInt(current.FeelsLikeC),
humidity: parseInt(current.humidity),
description: current.weatherDesc[0].value,
windMph: parseInt(current.windspeedMiles),
windKmh: parseInt(current.windspeedKmph),
precipMM: parseFloat(current.precipMM),
uvIndex: parseInt(current.uvIndex)
};
}
// Usage
// getWeatherWttr('San Francisco')
// .then(weather => {
// console.log(`${weather.location}: ${weather.description}`);
// console.log(`Temperature: ${weather.tempF}°F (feels like ${weather.feelsLikeF}°F)`);
// console.log(`Humidity: ${weather.humidity}%`);
// });
get_historical_weather
Get historical weather data (past dates).
# Historical weather for specific date range
LAT=51.5074
LON=-0.1278
START_DATE="2024-01-01"
END_DATE="2024-01-31"
curl -s "https://archive-api.open-meteo.com/v1/archive?latitude=${LAT}&longitude=${LON}&start_date=${START_DATE}&end_date=${END_DATE}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum" \
| jq '{
dates: .daily.time,
max_temps: .daily.temperature_2m_max,
min_temps: .daily.temperature_2m_min,
precipitation: .daily.precipitation_sum
}'
# Historical average for a month
curl -s "https://archive-api.open-meteo.com/v1/archive?latitude=${LAT}&longitude=${LON}&start_date=2024-01-01&end_date=2024-01-31&daily=temperature_2m_mean" \
| jq '[.daily.temperature_2m_mean[]] | add / length'
Node.js:
async function getHistoricalWeather(lat, lon, startDate, endDate) {
const params = new URLSearchParams({
latitude: lat.toString(),
longitude: lon.toString(),
start_date: startDate, // Format: YYYY-MM-DD
end_date: endDate,
daily: 'temperature_2m_max,temperature_2m_min,precipitation_sum,weather_code'
});
const res = await fetch(`https://archive-api.open-meteo.com/v1/archive?${params}`);
if (!res.ok) {
throw new Error(`Historical weather API failed: ${res.status}`);
}
const data = await res.json();
return data.daily.time.map((date, i) => ({
date,
maxTemp: data.daily.temperature_2m_max[i],
minTemp: data.daily.temperature_2m_min[i],
precipitation: data.daily.precipitation_sum[i],
weatherCode: data.daily.weather_code[i]
}));
}
// Usage
// getHistoricalWeather(40.7128, -74.0060, '2024-01-01', '2024-01-31')
// .then(history => {
// const avgTemp = history.reduce((sum, day) =>
// sum + (day.maxTemp + day.minTemp) / 2, 0) / history.length;
// console.log(`Average temperature in January: ${avgTemp.toFixed(1)}°C`);
// });
decode_weather_code
Decode Open-Meteo weather codes into human-readable descriptions.
# Weather code reference
decode_weather_code() {
case $1 in
0) echo "Clear sky" ;;
1|2|3) echo "Partly cloudy" ;;
45|48) echo "Foggy" ;;
51|53|55) echo "Drizzle" ;;
61|63|65) echo "Rain" ;;
71|73|75) echo "Snow" ;;
77) echo "Snow grains" ;;
80|81|82) echo "Rain showers" ;;
85|86) echo "Snow showers" ;;
95) echo "Thunderstorm" ;;
96|99) echo "Thunderstorm with hail" ;;
*) echo "Unknown" ;;
esac
}
# Usage
decode_weather_code 61 # Output: Rain
Node.js:
function decodeWeatherCode(code) {
const weatherCodes = {
0: 'Clear sky',
1: 'Mainly clear',
2: 'Partly cloudy',
3: 'Overcast',
45: 'Foggy',
48: 'Depositing rime fog',
51: 'Light drizzle',
53: 'Moderate drizzle',
55: 'Dense drizzle',
56: 'Light freezing drizzle',
57: 'Dense freezing drizzle',
61: 'Slight rain',
63: 'Moderate rain',
65: 'Heavy rain',
66: 'Light freezing rain',
67: 'Heavy freezing rain',
71: 'Slight snow fall',
73: 'Moderate snow fall',
75: 'Heavy snow fall',
77: 'Snow grains',
80: 'Slight rain showers',
81: 'Moderate rain showers',
82: 'Violent rain showers',
85: 'Slight snow showers',
86: 'Heavy snow showers',
95: 'Thunderstorm',
96: 'Thunderstorm with slight hail',
99: 'Thunderstorm with heavy hail'
};
return weatherCodes[code] || 'Unknown';
}
// Usage
// console.log(decodeWeatherCode(61)); // Output: Slight rain
weather_alerts_and_monitoring
Check weather conditions and generate alerts.
Node.js:
async function checkWeatherAlerts(lat, lon, thresholds) {
const {
maxTemp = 95, // °F
minTemp = 32, // °F
maxWindSpeed = 30, // mph
maxPrecip = 2 // inches
} = thresholds;
const weather = await getCurrentWeather(lat, lon, 'fahrenheit');
const forecast = await getWeatherForecast(lat, lon, 3, 'fahrenheit');
const alerts = [];
// Current condition alerts
if (weather.temperature >= maxTemp) {
alerts.push({
type: 'high_temp',
severity: 'warning',
message: `High temperature: ${weather.temperature}°F`
});
}
if (weather.temperature <= minTemp) {
alerts.push({
type: 'low_temp',
severity: 'warning',
message: `Low temperature: ${weather.temperature}°F`
});
}
if (weather.windSpeed >= maxWindSpeed) {
alerts.push({
type: 'high_wind',
severity: 'warning',
message: `High wind speed: ${weather.windSpeed} mph`
});
}
// Forecast alerts
forecast.forEach(day => {
if (day.precipitation >= maxPrecip) {
alerts.push({
type: 'heavy_rain',
severity: 'watch',
message: `Heavy rain expected on ${day.date}: ${day.precipitation} inches`,
date: day.date
});
}
});
return {
currentWeather: weather,
forecast: forecast,
alerts: alerts,
hasAlerts: alerts.length > 0
};
}
// Usage
// checkWeatherAlerts(40.7128, -74.0060, {
// maxTemp: 90,
// minTemp: 32,
// maxWindSpeed: 25,
// maxPrecip: 1.5
// }).then(result => {
// console.log(`Current: ${result.currentWeather.temperature}°F`);
// if (result.hasAlerts) {
// console.log('⚠️ Weather Alerts:');
// result.alerts.forEach(alert =>
// console.log(` - ${alert.severity.toUpperCase()}: ${alert.message}`)
// );
// } else {
// console.log('✅ No weather alerts');
// }
// });
Agent prompt
You have access to free weather APIs (Open-Meteo and wttr.in). When you need weather data:
1. For current weather and forecasts, use Open-Meteo:
- Current: https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t=temperature_2m,weather_code
- Forecast: Add &daily=temperature_2m_max,temperature_2m_min,precipitation_sum
- Historical: https://archive-api.open-meteo.com/v1/archive
2. For quick weather checks, use wttr.in:
- Simple: https://wttr.in/{city}?format=3
- JSON: https://wttr.in/{city}?format=j1
- Works with city names or coordinates
3. Weather parameters available:
- Temperature (current, feels-like, max/min)
- Humidity, precipitation, wind speed/direction
- Weather codes (0=clear, 61=rain, 71=snow, 95=thunderstorm)
- UV index, cloud cover, visibility
4. Temperature units:
- Open-Meteo: Add &temperature_unit=fahrenheit (default: celsius)
- wttr.in: Returns both Celsius and Fahrenheit
5. Best practices:
- Cache weather data for 15-30 minutes
- Use coordinates for accuracy (get from geocoding)
- Decode weather codes to human-readable descriptions
- Set reasonable alert thresholds for monitoring
No API keys required. Unlimited requests within fair use.
Cost analysis: Open-Meteo vs. paid APIs
Scenario: AI agent checking weather 1,000 times/month
| Provider | Monthly cost | Rate limits | API key required |
|---|---|---|---|
| OpenWeatherMap | $15-50 | 60 calls/min | ✅ Yes |
| WeatherAPI | $0-10 | 1M calls/month free | ✅ Yes |
| Open-Meteo | $0 | 10k req/day | ❌ No |
| wttr.in | $0 | Unlimited | ❌ No |
Annual savings with Open-Meteo: $180-$600
Rate limits / Best practices
- ✅ Open-Meteo: 10,000 requests/day — Very generous for typical use
- ✅ wttr.in: Unlimited — Community service with fair use
- ✅ Cache weather data — Weather doesn't change frequently (15-30 min cache)
- ✅ Use coordinates — More accurate than city names
- ✅ Decode weather codes — Convert numeric codes to descriptions
- ✅ Set timeouts — 10-second timeout for weather requests
- ⚠️ Self-host for critical apps — Open-Meteo is self-hostable
- ⚠️ Don't poll continuously — Weather updates hourly, not every second
Troubleshooting
Error: "Invalid coordinates"
- Symptom: API returns error for lat/lon
- Solution: Validate lat ∈ [-90, 90], lon ∈ [-180, 180]
No data returned:
- Symptom: API returns empty or null values
- Solution: Check if location has weather station coverage; try nearby coordinates
Historical data missing:
- Symptom: Archive API returns no data for date range
- Solution: Open-Meteo archive starts from 1940; check date format is YYYY-MM-DD
Weather code not recognized:
- Symptom: Unknown weather code number
- Solution: Refer to WMO weather code standard; codes 0-99 are defined
wttr.in returns HTML instead of JSON:
- Symptom: Response is HTML page
- Solution: Ensure you add ?format=j1 to get JSON response
Temperature unit confusion:
- Symptom: Unexpected temperature values
- Solution: Open-Meteo defaults to Celsius; add &temperature_unit=fahrenheit if needed
See also
- ../free-geocoding-and-maps/SKILL.md — Get coordinates for weather lookups
- ../send-email-programmatically/SKILL.md — Send weather alerts via email
- ../using-telegram-bot/SKILL.md — Send weather updates via Telegram