name: free-weather-data description: “使用免费的Open-Meteo和wttr.in API获取当前天气、预报和历史天气数据。适用于以下场景:(1) 获取天气条件,(2) 天气警报和监控,(3) 用于规划的预报数据,或(4) 气候分析。”
免费天气数据API — Open-Meteo & wttr.in
使用免费API获取当前天气、预报和历史天气数据。无需API密钥。尊重隐私的付费天气API替代方案(每月$5-100)。
为什么这可以替代付费天气API
💰 成本节省:
- ✅ 100% 免费 — 无需API密钥,无速率限制
- ✅ 全面数据 — 当前、预报、历史天气
- ✅ 开源 — Open-Meteo完全开源
- ✅ 隐私优先 — 无跟踪或数据收集
完美适用于需要以下功能的AI代理:
- 基于位置决策的天气条件
- 规划和调度的预报数据
- 分析用的历史天气数据
- 天气警报和监控
快速比较
| 服务 | 成本 | 速率限制 | 需要API密钥 | 隐私 |
|---|---|---|---|---|
| OpenWeatherMap | 每月$0-180 | 免费60次/分钟 | ✅ 是 | ❌ 有跟踪 |
| WeatherAPI | 每月$0-70 | 免费100万次/月 | ✅ 是 | ❌ 有跟踪 |
| Open-Meteo | 免费 | 每天1万次请求 | ❌ 否 | ✅ 私密 |
| wttr.in | 免费 | 无限 | ❌ 否 | ✅ 私密 |
技能
get_current_weather_open_meteo
使用Open-Meteo获取当前天气(最准确和全面)。
# 通过坐标获取当前天气
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
}'
# 带时区
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
};
}
// 使用示例
// 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
获取未来7-16天的天气预报。
# 7天预报
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
}'
# 未来24小时逐小时预报
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]
}));
}
// 使用示例
// 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
使用wttr.in获取简单天气(人类可读,速度极快)。
# 通过城市名获取天气(纯文本)
curl -s "https://wttr.in/London?format=3"
# 输出:London: ☀️ +22°C
# 获取详细天气报告
curl -s "https://wttr.in/Paris"
# JSON格式
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
}'
# 自定义格式(温度和条件)
curl -s "https://wttr.in/NewYork?format=%C+%t"
# 输出:Clear +75°F
# 通过坐标获取天气
curl -s "https://wttr.in/40.7128,-74.0060?format=%l:+%C+%t"
Node.js:
async function getWeatherWttr(location) {
// location可以是城市名或"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)
};
}
// 使用示例
// 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
获取历史天气数据(过去日期)。
# 特定日期范围的历史天气
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
}'
# 某月历史平均值
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, // 格式: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]
}));
}
// 使用示例
// 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
将Open-Meteo天气代码解码为人类可读描述。
# 天气代码参考
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
}
# 使用示例
decode_weather_code 61 # 输出: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';
}
// 使用示例
// console.log(decodeWeatherCode(61)); // 输出:Slight rain
weather_alerts_and_monitoring
检查天气条件并生成警报。
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 = [];
// 当前条件警报
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.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
};
}
// 使用示例
// 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');
// }
// });
代理提示
您有权访问免费天气API(Open-Meteo和wttr.in)。当需要天气数据时:
1. 对于当前天气和预报,使用Open-Meteo:
- 当前:https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t=temperature_2m,weather_code
- 预报:添加 &daily=temperature_2m_max,temperature_2m_min,precipitation_sum
- 历史:https://archive-api.open-meteo.com/v1/archive
2. 对于快速天气检查,使用wttr.in:
- 简单:https://wttr.in/{city}?format=3
- JSON:https://wttr.in/{city}?format=j1
- 适用于城市名或坐标
3. 可用天气参数:
- 温度(当前、体感、最高/最低)
- 湿度、降水、风速/风向
- 天气代码(0=晴朗,61=雨,71=雪,95=雷暴)
- UV指数、云量、能见度
4. 温度单位:
- Open-Meteo:添加 &temperature_unit=fahrenheit(默认:摄氏度)
- wttr.in:返回摄氏度和华氏度
5. 最佳实践:
- 缓存天气数据15-30分钟
- 使用坐标以提高准确性(从地理编码获取)
- 解码天气代码为人类可读描述
- 为监控设置合理的警报阈值
无需API密钥。在合理使用范围内无限请求。
成本分析:Open-Meteo vs. 付费API
场景:AI代理每月检查天气1,000次
| 提供商 | 月成本 | 速率限制 | 需要API密钥 |
|---|---|---|---|
| OpenWeatherMap | $15-50 | 60次/分钟 | ✅ 是 |
| WeatherAPI | $0-10 | 免费100万次/月 | ✅ 是 |
| Open-Meteo | $0 | 每天1万次请求 | ❌ 否 |
| wttr.in | $0 | 无限 | ❌ 否 |
使用Open-Meteo的年节省:$180-$600
速率限制 / 最佳实践
- ✅ Open-Meteo:每天10,000次请求 — 对典型使用非常慷慨
- ✅ wttr.in:无限 — 社区服务,合理使用
- ✅ 缓存天气数据 — 天气不频繁变化(缓存15-30分钟)
- ✅ 使用坐标 — 比城市名更准确
- ✅ 解码天气代码 — 将数字代码转换为描述
- ✅ 设置超时 — 天气请求10秒超时
- ⚠️ 关键应用自托管 — Open-Meteo可自托管
- ⚠️ 不要连续轮询 — 天气每小时更新,不是每秒
故障排除
错误:“无效坐标”
- 症状:API返回lat/lon错误
- 解决方案:验证lat ∈ [-90, 90], lon ∈ [-180, 180]
未返回数据:
- 症状:API返回空值或null
- 解决方案:检查位置是否有气象站覆盖;尝试附近坐标
历史数据缺失:
- 症状:存档API对日期范围无数据返回
- 解决方案:Open-Meteo存档从1940年开始;检查日期格式为YYYY-MM-DD
天气代码未识别:
- 症状:未知天气代码数字
- 解决方案:参考WMO天气代码标准;代码0-99已定义
wttr.in返回HTML而非JSON:
- 症状:响应为HTML页面
- 解决方案:确保添加 ?format=j1 以获取JSON响应
温度单位混淆:
- 症状:意外温度值
- 解决方案:Open-Meteo默认为摄氏度;如需华氏度添加 &temperature_unit=fahrenheit
参见
- …/free-geocoding-and-maps/SKILL.md — 获取天气查询的坐标
- …/send-email-programmatically/SKILL.md — 通过电子邮件发送天气警报
- …/using-telegram-bot/SKILL.md — 通过Telegram发送天气更新