polyweather OpenAPI
Programmatic access to live, minute-by-minute observed airport temperatures — the same sub-hourly runway-sensor data that leads the official METAR — plus today's-high tracking, METAR history and our next-METAR nowcast, across 36 airports. JSON over HTTPS, rolling window only.
The minute-level runway feed covers 26 of the 36; the 10 mainland-China airports are METAR + daily-high only — see Coverage.
Base URL
https://www.polyweather.today/api/open/v1
Authentication
Send your key in the Authorization header. Requests must come from an IP bound to the key, or you get 403 ip_not_allowed (the response's detail shows the detected IP — bind that one). Manage keys in the developer console.
Authorization: Bearer pmw_live_xxxxxxxx
Plans & limits
| Plan | Price | Rate | Monthly | IPs / key | Access |
|---|---|---|---|---|---|
| Basic | $49/mo | 60/min | 100,000 | 1 | cities, latest, window, daily-high, metar |
| Pro | $99/mo | 300/min | 1,000,000 | 3 | + nowcast, multi-city latest |
Every response carries X-RateLimit-Remaining / X-RateLimit-Remaining-Month; a 429 includes Retry-After. {city} = ICAO (RKSI) or slug (rksi); °C, UTC ISO-8601.
Coverage
Not every airport carries every feed, so GET /cities returns two booleans per city — runway and nowcast. Branch on those rather than hard-coding a list.
| Airports | runway_temp_c · /window · /nowcast | metar · daily-high |
|---|---|---|
| 26 — Asia (ex-China), Europe, US, Middle East | ✓ | ✓ |
| 10 — mainland China ZBAA, ZGGG, ZSPD, ZHHH, ZSQD, ZUUU, ZUCK, ZGSZ, ZSJN, ZHCC | — null / count 0 / null | ✓ |
Mainland China: our upstream provider withdrew minute-level runway-sensor access on 2026-07-07. For those 10 airports runway_temp_c and runway_at are null, /window returns count: 0, and /nowcast returns nowcast: null. METAR and daily-high tracking are unaffected and keep updating on each station's own cadence — the daily high those markets settle on is still there. If access is restored the two booleans flip back to true with no other change to the API.
Everywhere else a field can still be momentarily null during a feed gap, and nowcast is null whenever the feed is stale or the next METAR is outside the model's horizon. Treat null as “not available right now” — never as 0.
Quick start
curl -s "https://www.polyweather.today/api/open/v1/weather/RKSI/latest" \ -H "Authorization: Bearer pmw_live_your_key"
Code examples
import requests
BASE = "https://www.polyweather.today/api/open/v1"
KEY = "pmw_live_your_key" # create at https://www.polyweather.today/developers
headers = {"Authorization": f"Bearer {KEY}"}
# Latest observation for Incheon (runway temp leads the METAR)
r = requests.get(f"{BASE}/weather/RKSI/latest", headers=headers, timeout=10)
r.raise_for_status()
d = r.json()
print(d["runway_temp_c"], "->", d["metar_temp_c"], "high", d["daily_high_c"])
# Coverage is not uniform: skip cities with no runway feed instead of
# treating a null as a reading. See the Coverage section.
cities = requests.get(f"{BASE}/cities", headers=headers).json()["cities"]
fast = [c["code"] for c in cities if c["runway"]]
# Next-METAR nowcast (Pro) — null if the feed is stale or out of horizon
nc = requests.get(f"{BASE}/weather/RKSI/nowcast", headers=headers).json()["nowcast"]
if nc:
print(nc["pred_int"], "p", nc["p_hit"], nc["light"])const BASE = "https://www.polyweather.today/api/open/v1";
const KEY = "pmw_live_your_key"; // create at https://www.polyweather.today/developers
const headers = { Authorization: `Bearer ${KEY}` };
// Latest observation for Incheon (runway temp leads the METAR)
const res = await fetch(`${BASE}/weather/RKSI/latest`, { headers });
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const d = await res.json();
console.log(d.runway_temp_c, "->", d.metar_temp_c, "high", d.daily_high_c);
// Coverage is not uniform: skip cities with no runway feed instead of
// treating a null as a reading. See the Coverage section.
const { cities } = await (await fetch(`${BASE}/cities`, { headers })).json();
const fast = cities.filter((c) => c.runway).map((c) => c.code);
// Next-METAR nowcast (Pro) — null if the feed is stale or out of horizon
const { nowcast } = await (await fetch(`${BASE}/weather/RKSI/nowcast`, { headers })).json();
if (nowcast) console.log(nowcast.pred_int, "p", nowcast.p_hit, nowcast.light);Endpoints
List supported cities and their codes.
{
"count": 34,
"cities": [
{
"code": "RKSI",
"slug": "rksi",
"name": "Incheon",
"airport": "Incheon International Airport (RKSI)",
"beta": false,
"runway": true,
"nowcast": true
}
]
}code ICAO code — use as {city} in other callsslug also accepted as {city}name city nameairport airport full namebeta true = newer station, less-calibrated nowcastrunway false = no minute-level runway feed here: runway_temp_c is null and /window is empty (see Coverage)nowcast false = /nowcast returns null here (the nowcast is computed from the runway minutes)Newest observation for one city.
{
"city": "RKSI",
"name": "Incheon",
"runway_temp_c": 23.5,
"runway_at": "2026-09-14T02:26:00Z",
"metar_temp_c": 23,
"metar_at": "2026-09-14T02:00:00Z",
"daily_high_c": 24,
"daily_high_at": "2026-09-14T01:30:00Z",
"yesterday_high_c": 24,
"conditions": {
"wind_dir": 320,
"wind_kt": 10,
"sky": "BKN",
"sky_cover": 3,
"visibility_km": 10,
"pressure_hpa": 1019,
"dewpoint_c": 19
},
"source": "noaa",
"generated_at": "2026-09-14T02:27:12Z"
}runway_temp_c latest minute-level runway-sensor mean (°C) — leads the METAR; null where cities.runway is falserunway_at time of that runway reading (UTC)metar_temp_c latest official METAR temperature (whole °C)metar_at METAR observation time (UTC)daily_high_c today's running daily high — the number markets settle ondaily_high_at when today's high was reached (UTC)yesterday_high_c yesterday's daily high (context)conditions observed wind/sky/visibility/pressure/dewpoint — each field present only when reportedsource METAR source: noaa | amscgenerated_at snapshot build time (UTC)Rolling minute-by-minute series. Window only — no history beyond it.
limit — points to return, default 90, max 120{
"city": "RKSI",
"name": "Incheon",
"window_minutes": 90,
"runway_ends": ["15R", "16L"],
"count": 90,
"points": [
{
"at": "2026-09-14T02:26:00Z",
"ends": { "15R": 23.5, "16L": 23.5 },
"mean": 23.5
}
]
}window_minutes rolling window length — only the last ~90 min is exposedrunway_ends this airport's runway-end labelscount number of points returned — 0 where cities.runway is false (see Coverage)points[].at minute timestamp (UTC), newest firstpoints[].ends per-runway-end temperature (°C)points[].mean average across endsToday's running high so far.
{
"city": "RKSI",
"name": "Incheon",
"daily_high_c": 24,
"peaked_at": "2026-09-14T01:30:00Z",
"hourly_settlement": false,
"generated_at": "2026-09-14T02:27:12Z"
}daily_high_c today's running max (°C) — the settlement numberpeaked_at time of that high (UTC)hourly_settlement true = only on-the-hour readings count toward the high; readings between the hours are ignored. True for every China city except Beijing/Shanghai/Guangzhou.Recent METARs (parsed temp + raw text).
limit — count, default 8, max 24{
"city": "RKSI",
"name": "Incheon",
"metars": [
{
"at": "2026-09-14T02:00:00Z",
"temp_c": 23,
"raw": "METAR RKSI 140200Z 32010KT 9999 FEW025 BKN200 23/19 Q1019 NOSIG",
"source": "noaa"
}
]
}metars[].at observation time (UTC)metars[].temp_c METAR temperature (°C)metars[].raw raw METAR textmetars[].source data source(cadence) each station reports on its own schedule — typically half-hourly or hourly, and some issue off-schedule SPECIs. Poll on the interval you see in metars[].at rather than assuming 30 min; /daily-high's hourly_settlement tells you which stations settle on the hour.Next-METAR forecast: prediction, probability distribution, decision light.
{
"city": "RKSI",
"name": "Incheon",
"nowcast": {
"model": "anchor-v3",
"pred_int": 24,
"pred_float": 23.51,
"h_min": 4,
"next_metar_at": "2026-09-14T02:30:00Z",
"p_hit": 0.514,
"p_hit_raw": 0.482,
"gamma": 0.73,
"ladder": [
{ "t": 23, "p": 0.518 },
{ "t": 24, "p": 0.482 }
],
"light": "red"
},
"slope_30": 0,
"regime": "flat",
"forecast": {
"high_c": 25,
"peak_hour_local": "15:00",
"cloud_pct_peak": 57,
"models": [ { "name": "ECMWF", "high": 25.5 }, { "name": "GFS", "high": 23.6 } ],
"tomorrow_high_c": 24.6
}
}nowcast null when the city has no runway feed (cities.nowcast false), the feed is stale, or the next METAR is outside the model's horizonnowcast.pred_int predicted NEXT METAR, rounded (°C)nowcast.pred_float predicted, unroundednowcast.h_min horizon: minutes until the next METARnowcast.next_metar_at expected next METAR time (UTC)nowcast.p_hit calibrated probability pred_int is exactly rightnowcast.p_hit_raw same, before the daily monotone calibrationnowcast.gamma slope-damping factor applied at this hournowcast.ladder full probability distribution: { t: temp, p: probability }nowcast.light decision light green / yellow / red (signal reliability)slope_30 runway-temp slope, °C per 30 min (signed)regime flat | mid | fastforecast Open-Meteo model guidance: high_c, peak_hour_local, cloud_pct_peak, models[], tomorrow_high_cEvery city's latest in one call.
{
"count": 34,
"generated_at": "2026-09-14T02:27:12Z",
"cities": [
{
"city": "RKSI",
"name": "Incheon",
"runway_temp_c": 23.5,
"metar_temp_c": 23,
"daily_high_c": 24,
"daily_high_at": "2026-09-14T01:30:00Z"
}
]
}cities[] per city: runway_temp_c, metar_temp_c, daily_high_c, daily_high_atErrors
| HTTP | error | Meaning |
|---|---|---|
| 401 | missing_api_key / invalid_api_key / key_revoked | Bad or missing Authorization header. |
| 402 | no_active_api_plan | Plan lapsed — renew at /developers. |
| 403 | ip_not_allowed | Request IP isn't allowlisted. The detected IP is in `detail` — bind that IP. |
| 403 | plan_upgrade_required | Endpoint needs API Pro (nowcast, multi-city). |
| 404 | unknown_city / unknown_resource | Check the path. |
| 429 | rate_limited | Per-minute or monthly cap hit — see Retry-After. |
Getting started
API access is a paid add-on. Open the developer console to buy a plan (crypto, instant) and create an IP-locked key — your plan also unlocks the full web dashboard.
View-only data — not betting or financial advice. Rolling-window access; historical bulk export is not offered.