API Documentation
-
✨ API Version 2 — Expanded station support (30 stations), metric units for international locations, Wethr Low calculations, and explicit logic parameters. Base URL:
https://wethr.net/api/v2/Introduction
The Wethr.net API v2 provides programmatic access to weather observation data and model forecasts. It includes support for 30 weather stations across the US and international markets, with native metric units for non-US stations.
Base URL:
https://wethr.net/api/v2/
What's New in v2
- Data Quality Warnings NEW: The
wethr_highmode and the Pushobservationevent now include adata_qualityobject that flags missing temperature readings (the-999sentinel) in the trading-day window. It is keyed per resolution mode and escalates tocriticalwhen the station is currently down or a gap falls near the reported high/low. See Observations Data Quality and the Push Data Quality Flags. - Daily Summary Mode for Forecasts NEW: The Forecasts API now accepts
mode=daily, returning a per-model, per-day high/low summary bucketed by the station's local time. Atz_modeparameter selects the day-boundary convention (civilfor WU/Polymarket resolution,standardfor NWS/Kalshi). Only fully-covered days are returned; pin a historical run viarunfor backtesting. - Latest Run Mode for Forecasts NEW: The Forecasts API now accepts a
runparameter (latestor ISO 8601 timestamp). Userun=latest&model=HRRRto retrieve every forecast hour of the most recent run without needing to specify a time window — ideal for real-time model lookups. Responses include anX-Run-Timeheader identifying the resolved run cycle. - Push API BETA: Stream real-time observations, DSM/CLI releases, and temperature extreme alerts
- Model Accuracy API NEW DEV+: Per-model forecast accuracy metrics (MAE, bias, RMSE) across 66 stations with filtering by model, run time, lead time, and time window. Supports both daily highs and daily lows. Requires Developer or Enterprise tier.
- Nearby Stations API NEW BETA DEV+: Find weather stations near a target station (by radius) or look up an arbitrary list of stations, each with its latest observation. Useful for cross-referencing readings, building heat maps, or detecting microclimates. Requires Developer or Enterprise tier.
- Pacing API NEW BETA DEV+: See how the latest actual observation is running versus each model's forecast for the contract day, with the model temperature interpolated to the observation's exact time. Returns a low/high pace per model (plus an NWS row) in the station's native units — the same calculation shown on the dashboard. Requires Developer or Enterprise tier.
- Precipitation API BETA: New endpoint for monthly precipitation totals with real-time ASOS data
- NWS Forecasts API BETA: New endpoint for NWS hourly temperature forecasts with version history
- Expanded Station Support: 30 stations including London, Buenos Aires, Toronto, and Seoul
- Metric Units: International stations (EGLC, SAEZ, CYYZ, RKSI, NZWN, SBGR, LFPB, LTAC, VILK, EDDM, LLBG, RJTT, ZSPD, WSSS, EPWA, LIMC, LEMD, ZUUU, ZBAA, ZHHH, ZUCK, ZGSZ, CYUL, CYHC, LFPO, YSSY, UUWW, RCSS, LTFM, MMMX, VHKO, EFHK, EHAM, RKPK, WIHH, WMKK, MPMG, DNMM, OEJN, FACT, OPKC, ZGGG, RPLL, ZSQD) return temperatures in Celsius
- Wethr Low: The
wethr_highmode now also returnswethr_low - Improved Wethr High/Low Accuracy (NWS mode): NWS logic incorporates high-frequency observations to capture extremes between standard METAR reporting intervals. WU/Polymarket logic uses only standard METAR observations.
- Required Logic Parameter: Explicit
logicparameter required forwethr_highmode (nwsorwu) - Observation Type Filter: Filter
latestmode bydsm_high,dsm_low,cli_high,cli_low - Units Indicator: All responses include a
unitsfield - One Minute Observations (OMO) COMING SOON: Raw One Minute Observation data is not yet available via the API (REST or Push). We anticipate making OMO data available in the coming months. However, Wethr High and Wethr Low values already incorporate OMO data and update in real time when a one minute observation impacts the calculated high or low.
Authentication
All requests require an API key. Include it in the HTTP header:
Authorization: Bearer <YOUR_API_KEY>Or use the custom header:
X-API-Key: <YOUR_API_KEY>Generate API keys in your Account Settings.
Rate Limiting
Tier Requests/Minute Requests/Day Professional 60 5,000 Developer 300 100,000 Enterprise 1200 500,000
Supported Stations
US Stations (Fahrenheit)
Code Location Timezone KMDWChicago Midway America/Chicago KPHLPhiladelphia America/New_York KMIAMiami America/New_York KLAXLos Angeles America/Los_Angeles KBKFAurora (Buckley SFB) America/Denver KDENDenver America/Denver KAUSAustin America/Chicago KNYCNew York (Central Park) America/New_York KDFWDallas/Fort Worth America/Chicago KMSYNew Orleans America/Chicago KLGANew York LaGuardia America/New_York KDALDallas Love Field America/Chicago KHOUHouston Hobby America/Chicago KSEASeattle America/Los_Angeles KSFOSan Francisco America/Los_Angeles KLASLas Vegas America/Los_Angeles KPHXPhoenix America/Phoenix KSATSan Antonio America/Chicago KDCAWashington D.C. America/New_York KCLTCharlotte America/New_York KBOSBoston America/New_York KBNANashville America/Chicago KATLAtlanta America/New_York KJAXJacksonville America/New_York KOKCOklahoma City America/Chicago KDTWDetroit America/Detroit KMSPMinneapolis America/Chicago International Stations (Celsius) NEW
Code Location Timezone EGLCLondon City Airport Europe/London SAEZBuenos Aires Ezeiza America/Argentina/Buenos_Aires CYYZToronto Pearson America/Toronto RKSISeoul Incheon Asia/Seoul SBGRSão Paulo Guarulhos America/Sao_Paulo LFPBParis Le Bourget Europe/Paris LTACEsenboğa International Airport Europe/Istanbul VILKChaudhary Charan Singh International Airport Asia/Kolkata EDDMMunich Airport Europe/Berlin LLBGBen Gurion International Airport Asia/Jerusalem RJTTTokyo Haneda Airport Asia/Tokyo ZSPDShanghai Pudong Airport Asia/Shanghai WSSSSingapore Changi Airport Asia/Singapore EPWAWarsaw Chopin Airport Europe/Warsaw LIMCMilan Malpensa Airport Europe/Rome LEMDMadrid Barajas Airport Europe/Madrid ZUUUChengdu Shuangliu Airport Asia/Shanghai ZBAABeijing Capital Airport Asia/Shanghai ZHHHWuhan Tianhe Airport Asia/Shanghai ZUCKChongqing Jiangbei Airport Asia/Shanghai ZGSZShenzhen Bao'an Airport Asia/Shanghai CYULMontreal Trudeau Airport America/Toronto CYHCVancouver Harbour America/Vancouver LFPOParis Orly Airport Europe/Paris YSSYSydney Kingsford Smith Airport Australia/Sydney UUWWMoscow Vnukovo Airport Europe/Moscow RCSSTaipei Songshan Airport Asia/Taipei LTFMIstanbul Airport Europe/Istanbul MMMXMexico City International Airport America/Mexico_City VHKOHong Kong Observatory Asia/Hong_Kong EFHKHelsinki-Vantaa Airport Europe/Helsinki EHAMAmsterdam Schiphol Airport Europe/Amsterdam RKPKGimhae International Airport Asia/Seoul WIHHHalim Perdanakusuma Airport Asia/Jakarta WMKKKuala Lumpur International Airport Asia/Kuala_Lumpur MPMGMarcos Gelabert Airport America/Panama DNMMLagos/Ikeja Airport Africa/Lagos OEJNJeddah/King Abdulaziz Airport Asia/Riyadh FACTCape Town/DF Malan Airport Africa/Johannesburg OPKCKarachi International Airport Asia/Karachi ZGGGGuangzhou/Baiyun Airport Asia/Shanghai RPLLNinoy Aquino International Airport Asia/Manila ZSQDQingdao/Tsingtao Airport Asia/Shanghai
Error Responses
- 400 Bad Request — Invalid or missing parameters
- 401 Unauthorized — Invalid or missing API key
- 403 Forbidden — Tier restriction (e.g., Model Accuracy API requires Developer or Enterprise)
- 429 Too Many Requests — Rate limit exceeded
- 500 Server Error — Internal server error
{ "error": "Missing required parameter: logic", "details": "The 'logic' parameter is required for wethr_high mode. Valid values: 'nws' or 'wu'." }
Observations API
GET/api/v2/observations.phpNote — One Minute Observations (OMO): Raw OMO data is not currently available through the Observations REST API. We anticipate adding support for One Minute Observations in the coming months. Currently, only standard METAR, HF-METAR, and SPECI observations are returned. However, Wethr High and Wethr Low values (mode=wethr_high) already incorporate OMO data and update in real time when a one minute observation impacts the calculated high or low.Mode: Latest Observation
Returns the most recent observation for a station.
Parameters
Parameter Required Description station_codeYes Station identifier modeYes Set to latestobservation_typeNo Filter: dsm_high,dsm_low,cli_high,cli_lowExample Request
GET /api/v2/observations.php?station_code=KMDW&mode=latest GET /api/v2/observations.php?station_code=KMDW&mode=latest&observation_type=cli_highExample Response
{ "station_code": "KMDW", "observation_time": "2025-06-15 18:53:00", "temperature": 26.7, "temperature_display": 80.1, "lowest_probable": 80, "highest_probable": 80, "units": "fahrenheit", "dsm_high": 28.3, "dsm_high_display": 83, "relative_humidity": 55.2, "altimeter": 1019.8, "sea_level_pressure": 1019.6 }
Mode: Wethr High/Low Calculation
Calculates the current trading-day high and low temperatures based on multiple observation sources.
- NWS logic: Uses CLI/DSM reports, 6-hour highs/lows, and all available observations to capture extremes between standard reporting intervals. Time window follows Standard Time year-round.
- WU logic: Uses only standard METAR observations. Time window follows Local Time year-round. See Precision Validation for US Stations for details on how observation accuracy is ensured.
Parameters
Parameter Required Description station_codeYes Station identifier modeYes Set to wethr_highlogicYes nws— NWS/Kalshi: Standard Time year-round, uses CLI/DSM/6hr data
wu— Weather Underground: Local Time year-round, uses exact temperatureExample Request
GET /api/v2/observations.php?station_code=KMDW&mode=wethr_high&logic=nwsExample Response
{ "station_code": "KMDW", "date": "2025-06-15", "wethr_high": 83, "wethr_low": 68, "time_of_high_utc": "2025-06-15 20:53:00", "time_of_low_utc": "2025-06-15 10:53:00", "calculation_logic": "nws", "units": "fahrenheit", "data_quality": { "nws": {"ok": true, "warnings": []} } }Data Quality NEW
The
wethr_highresponse includes adata_qualityobject that flags missing temperature readings (the-999sentinel the station logs when a sensor does not report) within the trading-day window. Missing readings are simply absent from the high/low calculation, so a gap can leave the reportedwethr_high/wethr_lowunderstated — this object tells you when coverage was incomplete.It is keyed by the
logicyou requested (nwsorwu), since each mode scans its own time window. When the day's observations are complete,okistrueandwarningsis empty.{ "data_quality": { "nws": { "ok": false, "warnings": [ { "code": "temperature_data_missing", "severity": "critical", "message": "2 temperature readings missing (-999) in this window; longest gap 2 consecutive (~10 min) — a gap falls within ~60 min of the day's high, so wethr_high/wethr_low may be unreliable.", "missing_count": 2, "longest_run": 2, "longest_run_minutes": 10, "first_missing_utc": "2025-06-15 20:43:00", "last_missing_utc": "2025-06-15 20:48:00", "active": false, "active_run": 0, "near_extreme": true, "near_high": true, "near_low": false } ] } } }The
temperature_data_missingwarning object and its severity rules are identical to the one delivered through the Push API. See the Push Data Quality Flags section for the full field reference.WU Logic: Precision Validation for US Stations
METAR observations report temperature in two ways: the body of the report contains a whole-degree Celsius value (e.g.,
26/08), while the remarks section may contain a "T-group" with tenths-of-a-degree precision (e.g.,T02560078= 25.6°C). The difference matters because26°Crounds to79°F, while25.6°Crounds to78°F— a 1°F discrepancy from a single observation that can affect the Wethr High.US ASOS stations (ICAO codes beginning with
K) nearly always produce T-groups. However, when a new METAR is first published, the body temperature may be available before the complete remarks section has propagated through all data sources. During this brief window, the observation may appear to have a rounded whole-degree temperature when a more precise value is forthcoming.To prevent this from affecting the Wethr High/Low calculation, the API applies the following validation when using
logic=wu:- If a K-station observation has tenths-of-a-degree precision (T-group present), it is used immediately — the temperature is exact and unambiguous.
- If a K-station observation has only whole-degree precision (no T-group), the API requires confirmation from a secondary data source before including it in the Wethr High/Low calculation. This ensures the T-group has had time to propagate and that the rounded value is not premature.
This validation applies only to WU logic. NWS logic is unaffected because it uses temperature ranges (
lowest_probable/highest_probable) rather than a single rounded value, which inherently accounts for rounding uncertainty.
Mode: History
Retrieves observations over a time range (max 24 hours). Default mode when
modeis omitted.Parameters
Parameter Required Description station_codeYes Station identifier start_timeYes Start of range (ISO 8601 UTC) end_timeYes End of range (ISO 8601 UTC) Example Request
GET /api/v2/observations.php?station_code=KMDW&start_time=2025-06-15T00:00:00Z&end_time=2025-06-15T12:00:00Z
Observation Data Model
Field Type Description idinteger Unique record identifier station_codestring Station identifier observation_timedatetime UTC timestamp temperaturedecimal Temperature in Celsius (raw) temperature_displaydecimal Temperature in station's native units lowest_probableinteger Min probable temp (native units) highest_probableinteger Max probable temp (native units) unitsstring fahrenheitorcelsiusprecision_levelinteger Data precision (1 = exact) dew_pointdecimal Dew point (Celsius) relative_humiditydecimal Relative humidity (%) wind_directionstring Wind direction wind_speeddecimal Wind speed (knots) wind_gustdecimal Wind gust (knots) visibilitydecimal Visibility (statute miles) altimeterdecimal Altimeter setting in hectopascals (hPa). Divide by 33.8639 to convert to inches of mercury (inHg). sea_level_pressuredecimal Sea level pressure in hectopascals (hPa). Reported in METAR remarks when available; may be nullfor observations without an SLP group.six_hour_highdecimal 6-hour high (Celsius) six_hour_lowdecimal 6-hour low (Celsius) cli_high/cli_lowdecimal CLI report values (US only) dsm_high/dsm_lowdecimal DSM report values (US only) *_displayinteger Values in station's native units *_finteger Values in Fahrenheit (backward compat) Client Examples
cURL — Wethr High with NWS Logic
curl -G "https://wethr.net/api/v2/observations.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW" \ --data-urlencode "mode=wethr_high" \ --data-urlencode "logic=nws"cURL — Latest CLI High
curl -G "https://wethr.net/api/v2/observations.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW" \ --data-urlencode "mode=latest" \ --data-urlencode "observation_type=cli_high"JavaScript — Wethr High/Low
const params = new URLSearchParams({ station_code: 'KMDW', mode: 'wethr_high', logic: 'nws' }); fetch(`/api/v2/observations.php?${params}`, { headers: { 'Authorization': `Bearer ${apiKey}` } }) .then(res => res.json()) .then(data => { console.log(`High: ${data.wethr_high}° / Low: ${data.wethr_low}°`); });Python — Latest Observation
import requests response = requests.get( 'https://wethr.net/api/v2/observations.php', params={'station_code': 'KMDW', 'mode': 'latest'}, headers={'Authorization': f'Bearer {api_key}'} ) data = response.json() print(f"Current: {data['temperature_display']}°{data['units'][0].upper()}")
Forecasts API
GET/api/v2/forecasts.phpRetrieves model forecast data for a location within a time range.
Parameters
Parameter Required Description location_nameYes Location identifier (e.g., KMDW)start_valid_timeConditional Start of forecast range (ISO 8601). Required unless a runfilter is specified.end_valid_timeConditional End of forecast range (ISO 8601). Required unless a runfilter is specified.modelConditional Filter by model: ARPEGE,ECMWF-IFS,GEFS,GEM-GDPS,GEM-HRDPS,GFS,GFS-MOS,HRRR,ICON,JMA,LAV-MOS,NAM,NAM-MOS,NAM4KM,NBM,NBS-MOS,RAP,UKMO. Optional in normal mode; required whenrun=latest.runNEWNo Filter results to a single model run. Accepts: latest— resolves to the most recent run for the specifiedmodel+location_name. When used,modelbecomes required.- An ISO 8601 timestamp matching a specific
run_time(e.g.,2026-04-20T13:00:00Z).
runfilter is specified, thestart_valid_time/end_valid_timewindow becomes optional (omit to receive every forecast hour the run produced; supply it to filter the run's output to a subset). Omit the parameter entirely for default behavior (returns all runs intersecting the requested window).modeNEWNo Set to dailyto receive a per-model, per-day high/low summary instead of raw hourly rows. Days are bucketed by the station's local time, and a day is returned only when the model run covers it completely. See Daily Summary Mode. Omit for default (raw hourly) output.tz_modeNEWNo Day-boundary convention for mode=daily. Ignored in all other modes. Accepts:civil(default) — local wall-clock time, adjusting for DST. Day boundaries follow the local civil day (23, 24, or 25 hours across DST transitions). Matches WU / Polymarket resolution.standard— Local Standard Time year-round, no DST adjustment. Every day is exactly 24 hours. Matches NWS climate reporting and Kalshi temperature market resolution.
Example Request
GET /api/v2/forecasts.php?location_name=KMDW&start_valid_time=2025-06-15T18:00:00Z&end_valid_time=2025-06-15T20:00:00Z&model=HRRRLatest Run Mode NEW
Passing
run=latestreturns every forecast hour of the most recent run for a givenmodelandlocation_name, without requiring the caller to know the model's run schedule or forecast horizon. This is the recommended way to retrieve "the latest HRRR for KAUS" and similar real-time lookups.Behavior:
modelis required. This prevents ambiguity across models that update on different cadences (e.g., HRRR hourly vs. GFS every 6 hours).start_valid_timeandend_valid_timeare optional. Omit them to receive every forecast hour the run produced (F00 through the model's maximum lead time). Supply them to filter the run's output to a specific window (e.g., "latest HRRR, but only the next 6 hours").- The response includes an
X-Run-Timeheader containing the resolved run's initialization time (UTC), so clients can confirm which cycle they received without parsing rows. - If no runs exist for the specified model/location, the endpoint returns an empty array (
[]) with HTTP 200.
Example Request — Latest HRRR Run (All Forecast Hours)
GET /api/v2/forecasts.php?location_name=KAUS&model=HRRR&run=latestExample Request — Specific Run by Timestamp
Retrieve a specific historical run by passing an ISO 8601 timestamp to
run. The window is optional; omit it to get every forecast hour the run produced:GET /api/v2/forecasts.php?location_name=KAUS&model=HRRR&run=2026-04-20T13:00:00ZDaily Summary Mode NEW
Passing
mode=dailycollapses the hourly forecast into a per-model, per-day high and low, bucketed by the station's local time. Instead of one row per forecast hour, you get one row per model per day. This is intended for daily temperature brackets and model backtesting, where the unit of interest is the day's extreme rather than the hourly trace.Behavior:
- Local-time days. Each forecast hour (stored in UTC) is converted to the station's local time using its IANA timezone, then grouped by local calendar day. The day boundary convention is controlled by
tz_mode(civil, the default, orstandard). - Complete days only. A day is returned only when the model run fully covers it — that is, the run's forecast hours span the entire local day from midnight to midnight. Partial days at the start of a run (which begins mid-day) and at the end of its forecast horizon are omitted. A single run that fully covers several days returns one row per covered day.
- Run selection. By default, each model is summarized at its latest run, so
modelis optional (omit it to summarize every available model; supply it to restrict to one). To summarize a specific historical run instead — for example, when backtesting — pass that run's timestamp viarun(e.g.,run=2026-06-01T12:00:00Z); the summary then reflects only that run. - All models included. No model is filtered out. Gridded models (e.g.,
GFS,HRRR,NAM4KM) and statistical guidance (e.g.,GFS-MOS,NBS-MOS) are summarized alike. Because guidance products are often issued at coarser intervals, their high/low is derived from fewer points; thepoints_usedfield reports how many forecast points contributed to each day so you can account for sampling resolution. - Response headers. An
X-Run-Signatureheader identifies the set of runs used to build the summary, so the cached response rotates automatically when a newer run lands. - If no fully-covered day exists for the station (for example, when stored runs do not yet extend far enough), the endpoint returns an empty array (
[]) with HTTP 200. - If the station's timezone is not configured in the catalog, the endpoint returns HTTP 422 rather than silently bucketing in UTC.
Example Request — Daily High/Low, All Models
GET /api/v2/forecasts.php?location_name=KMDW&mode=dailyExample Request — Single Model, Standard-Time Days
GET /api/v2/forecasts.php?location_name=KMDW&mode=daily&model=GFS&tz_mode=standardDaily Summary Fields
Field Type Description modelstring Forecast model name datestring Local calendar day ( YYYY-MM-DD) in the station's timetimezonestring IANA timezone used for bucketing (e.g., America/Chicago)tz_modestring Day-boundary convention applied: civilorstandardrun_timedatetime Model run the summary was derived from (UTC) points_usedinteger Number of forecast points that contributed to this day's high/low high_fdecimal Day's maximum temperature (Fahrenheit) low_fdecimal Day's minimum temperature (Fahrenheit) high_cdecimal Day's maximum temperature (Celsius) low_cdecimal Day's minimum temperature (Celsius) Note: Fahrenheit fields are present for US stations and Celsius fields for international stations, mirroring the native units of the underlying forecast data; a field is omitted for a day when the source model did not provide that unit.
Example Response
[ { "model": "GFS", "date": "2026-06-02", "timezone": "America/Chicago", "tz_mode": "civil", "run_time": "2026-06-01 06:00:00", "points_used": 24, "high_f": 73.1, "low_f": 56.4, "high_c": 22.8, "low_c": 13.5 }, { "model": "NAM4KM", "date": "2026-06-02", "timezone": "America/Chicago", "tz_mode": "civil", "run_time": "2026-06-01 12:00:00", "points_used": 24, "high_f": 71.3, "low_f": 55.1, "high_c": 21.8, "low_c": 12.8 } ]Tip: For backtesting which model is most accurate at a given lead time, pin a historical run with
runand read the returneddatevalues against that run'srun_time: the gap between them is the lead time, and a single run covering multiple days lets you score day+1, day+2, and beyond from one response.Forecast Data Model
Field Type Description idinteger Unique record identifier modelstring Forecast model name location_namestring Location identifier latitudedecimal Latitude longitudedecimal Longitude run_timedatetime Model run time (UTC) valid_timedatetime Forecast valid time (UTC) forecast_hourinteger Lead time in hours temperature_kdecimal Temperature (Kelvin) temperature_fdecimal Temperature (Fahrenheit) temperature_cdecimal Temperature (Celsius) relative_humidityNEWdecimal Relative humidity (%) dew_point_fNEWdecimal Dew point (Fahrenheit) dew_point_cNEWdecimal Dew point (Celsius) apparent_temp_fNEWdecimal Apparent ("feels like") temperature (Fahrenheit) apparent_temp_cNEWdecimal Apparent ("feels like") temperature (Celsius) wind_speed_mphNEWdecimal Wind speed (mph) wind_speed_kmhNEWdecimal Wind speed (km/h) wind_direction_degNEWinteger Wind direction (degrees, 0–360) wind_gusts_mphNEWdecimal Wind gusts (mph) wind_gusts_kmhNEWdecimal Wind gusts (km/h) cloud_coverNEWdecimal Total cloud cover (%) precipitation_mmNEWdecimal Precipitation (millimeters) precipitation_inNEWdecimal Precipitation (inches) weather_codeNEWinteger WMO weather interpretation code pressure_msl_hpaNEWdecimal Mean sea-level pressure (hPa) surface_pressure_hpaNEWdecimal Surface pressure (hPa) inserted_atdatetime Timestamp when this forecast record was ingested into the database (UTC). Useful as a freshness indicator for weighting or staleness calculations. Note: The extended fields (humidity, dew point, apparent temperature, wind, cloud cover, precipitation, weather code, and pressure) are populated where the source model provides them. A field will be
nullfor any model/run that does not supply that variable, so clients should treat all extended fields as nullable. The temperature fields are present for all models.Example Response
[ { "id": 123456, "model": "HRRR", "location_name": "KMDW", "latitude": 41.786, "longitude": -87.752, "run_time": "2025-06-15 18:00:00", "valid_time": "2025-06-15 19:00:00", "forecast_hour": 1, "temperature_k": 299.82, "temperature_f": 80.0, "temperature_c": 26.5, "relative_humidity": 55.0, "dew_point_f": 62.5, "dew_point_c": 16.9, "apparent_temp_f": 82.4, "apparent_temp_c": 28.0, "wind_speed_mph": 11.5, "wind_speed_kmh": 18.5, "wind_direction_deg": 210, "wind_gusts_mph": 18.2, "wind_gusts_kmh": 29.3, "cloud_cover": 40.0, "precipitation_mm": 0.0, "precipitation_in": 0.0, "weather_code": 2, "pressure_msl_hpa": 1014.2, "surface_pressure_hpa": 1009.8, "inserted_at": "2025-06-15 18:42:13" }, { "id": 123457, "model": "HRRR", "location_name": "KMDW", "latitude": 41.786, "longitude": -87.752, "run_time": "2025-06-15 18:00:00", "valid_time": "2025-06-15 20:00:00", "forecast_hour": 2, "temperature_k": 300.37, "temperature_f": 81.0, "temperature_c": 27.2, "relative_humidity": 53.0, "dew_point_f": 62.1, "dew_point_c": 16.7, "apparent_temp_f": 83.1, "apparent_temp_c": 28.4, "wind_speed_mph": 12.1, "wind_speed_kmh": 19.5, "wind_direction_deg": 215, "wind_gusts_mph": null, "wind_gusts_kmh": null, "cloud_cover": 35.0, "precipitation_mm": 0.0, "precipitation_in": 0.0, "weather_code": 2, "pressure_msl_hpa": 1014.0, "surface_pressure_hpa": 1009.6, "inserted_at": "2025-06-15 18:42:13" } ]Tip: The
run_timefield represents the model initialization cycle (e.g.,18:00:00for an 18z run). Combined withinserted_at, you can determine both which cycle a forecast belongs to and how recently it was ingested — useful for staleness decay or freshness-weighted blending across models. Both fields are in UTC.Client Examples
cURL — Forecast Range
curl -G "https://wethr.net/api/v2/forecasts.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "location_name=KMDW" \ --data-urlencode "start_valid_time=2025-06-15T18:00:00Z" \ --data-urlencode "end_valid_time=2025-06-15T20:00:00Z" \ --data-urlencode "model=HRRR"cURL — Latest Run (All Forecast Hours)
curl -G "https://wethr.net/api/v2/forecasts.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "location_name=KAUS" \ --data-urlencode "model=HRRR" \ --data-urlencode "run=latest"cURL — Daily High/Low Summary (All Models)
curl -G "https://wethr.net/api/v2/forecasts.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "location_name=KMDW" \ --data-urlencode "mode=daily"Python — Latest Run
import requests response = requests.get( 'https://wethr.net/api/v2/forecasts.php', params={'location_name': 'KAUS', 'model': 'HRRR', 'run': 'latest'}, headers={'Authorization': f'Bearer {api_key}'} ) forecasts = response.json() resolved_run = response.headers.get('X-Run-Time') print(f"Latest HRRR run: {resolved_run} ({len(forecasts)} forecast hours)")
Precipitation API BETA
GET/api/v2/precipitation.php⚠️ Beta Notice: This API is currently in beta testing. While functional, you may encounter bugs or unexpected behavior. Data accuracy is not guaranteed during this phase. Please report any issues to help us improve the service.Returns current month precipitation totals for a station, combining official CLI (Climate Report) data with live ASOS observations. All date/time calculations use Local Standard Time (no DST adjustment) year-round, matching NWS climate reporting standards.
Endpoint & Parameters
Parameter Required Description station_codeYes Station identifier (see supported stations below) Note: This endpoint always returns data for the current month. Historical month data is not available via this API.
Supported Stations
KNYC,KLAX,KMDW,KSFO,KAUS,KMIA,KDFW,KHOU,KDEN,KBKF,KSEA,KPHL,KLAS,KPHX,KSAT,KDCA,KCLT,KBOS,KBNA,KATL,KJAX,KOKC,KDTW,KMSP,KMSYExample Request
GET /api/v2/precipitation.php?station_code=KAUSExample Response
{ "station_code": "KAUS", "station_name": "Austin", "timezone": "America/Chicago", "timezone_offset_hours": -6, "today_local_date": "2025-06-15", "month": 6, "year": 2025, "official_mtd": 1.45, "today_precip": 0.127, "total_mtd": 1.58, "has_trace": false, "last_trace_time": null, "cli_date": "2025-06-14", "cli_issued_at": "2025-06-15 05:15:00", "today_hourly_max": { "2025-06-15 08": 0.05, "2025-06-15 09": 0.077 }, "timestamp": "2025-06-15T21:30:00Z", "units": "inches" }Response Fields
Field Type Description station_codestring Station identifier station_namestring Human-readable station name timezonestring PHP timezone identifier timezone_offset_hoursinteger Fixed UTC offset for Local Standard Time (e.g., -6 for CST) today_local_datestring Current date in station's Local Standard Time (YYYY-MM-DD) monthinteger Current month (1-12) yearinteger Current year official_mtddecimal Official month-to-date total from CLI reports (through yesterday) today_precipdecimal Today's precipitation calculated from live ASOS observations total_mtddecimal Combined total: official_mtd + today_preciphas_traceboolean Whether trace precipitation (P0000) was reported today last_trace_timestring|null Time of last trace report (Local Standard Time) cli_datestring|null Date of the most recent CLI report used cli_issued_atstring|null Timestamp when the CLI report was issued today_hourly_maxobject Hourly precipitation breakdown for today (hour → max precip) timestampstring API response timestamp (UTC, ISO 8601) unitsstring Always inchesUnderstanding the Data
- Official (CLI): The
official_mtdvalue comes from the NWS Daily Climate Report (CLI), typically issued around 5 AM local time. This is the "banked" official total through the previous day. - Live (Today): The
today_precipvalue is calculated in real-time from 5-minute ASOS observations since midnight Local Standard Time. - Trace: When
has_traceis true, precipitation was detected but was too small to measure (<0.01"). Trace amounts are not included in the totals. - Hourly Max: The
today_hourly_maxobject shows the maximum cumulative reading for each hour, useful for detailed analysis.
cURL Example
curl -G "https://wethr.net/api/v2/precipitation.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KAUS"Python Example
import requests response = requests.get( 'https://wethr.net/api/v2/precipitation.php', params={'station_code': 'KAUS'}, headers={'Authorization': f'Bearer {api_key}'} ) data = response.json() print(f"Month-to-Date: {data['total_mtd']}\"") print(f" Official (CLI): {data['official_mtd']}\"") print(f" Today (Live): {data['today_precip']}\"") if data['has_trace']: print(f" Trace detected at {data['last_trace_time']}")
NWS Forecasts API BETA
GET/api/v2/nws_forecasts.php⚠️ Beta Notice: This API is currently in beta testing. While functional, you may encounter bugs or unexpected behavior. Please report any issues to help us improve the service.Returns NWS hourly temperature forecasts for a station. Forecasts are sourced from the National Weather Service API and stored with version tracking as they update throughout the day.
⚠️ Critical: Local Standard Time Convention
All dates and hours in this API use Local Standard Time year-round (no Daylight Saving Time adjustment). This means:- During DST months, the "day" boundaries are shifted by 1 hour compared to civil time
- Hour 0 always represents midnight Standard Time, not midnight local civil time
- This convention matches NWS climate reporting and Kalshi temperature market resolution
If you are using a different resolution source that uses civil time (with DST), these forecasts may not align correctly with your day boundaries.
Endpoint & Parameters
Parameter Required Description station_codeYes Station identifier dateNo Forecast date in YYYY-MM-DD format. Defaults to today. Can request up to 2 days in the future or 365 days (1 year) in the past. modeNo latest(default) — Returns only the most recent forecast version for the requested date
history— Returns all forecast versions for the requested date (useful to see how the forecast evolved)Example Requests
# Today's latest forecast GET /api/v2/nws_forecasts.php?station_code=KAUS # Tomorrow's latest forecast GET /api/v2/nws_forecasts.php?station_code=KAUS&date=2025-06-16 # Today's forecast history (all versions) GET /api/v2/nws_forecasts.php?station_code=KAUS&date=2025-06-15&mode=historyExample Response (mode=latest)
{ "station_code": "KAUS", "station_name": "Austin", "timezone": "America/Chicago", "timezone_offset_hours": -6, "forecast_date": "2025-06-15", "version": 12, "hourly_temps": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, 94, 95, 95, 94, 92, 89, 85, 82, 79, 77, 75], "high": 95, "low": 75, "timestamp": "2025-06-15T21:30:00Z", "units": "fahrenheit", "time_convention": "local_standard_time" }Example Response (mode=history)
{ "station_code": "KAUS", "station_name": "Austin", "timezone": "America/Chicago", "timezone_offset_hours": -6, "forecast_date": "2025-06-15", "total_versions": 3, "forecasts": [ { "version": 1, "hourly_temps": [null, null, null, null, null, null, 71, 74, 78, 82, ...], "high": 94, "low": 71 }, { "version": 2, "hourly_temps": [null, null, null, null, null, null, null, null, null, null, null, null, 91, 93, ...], "high": 95, "low": 77 } ], "timestamp": "2025-06-15T21:30:00Z", "units": "fahrenheit", "time_convention": "local_standard_time" }Response Fields
Field Type Description station_codestring Station identifier station_namestring Human-readable station name timezonestring PHP timezone identifier timezone_offset_hoursinteger Fixed UTC offset for Local Standard Time forecast_datestring Forecast date (YYYY-MM-DD) in Local Standard Time versioninteger Forecast version number (latest mode only) hourly_tempsarray Array of 25 temperatures (indices 0-24, where 24 = midnight next day). Values are nullfor hours that had already passed when the forecast was issued.highinteger|null Forecasted high temperature, calculated as the maximum of all non-null values in hourly_tempslowinteger|null Forecasted low temperature, calculated as the minimum of all non-null values in hourly_tempstotal_versionsinteger Number of forecast versions available (history mode only) forecastsarray Array of all forecast versions (history mode only) unitsstring Always fahrenheittime_conventionstring Always local_standard_timeUnderstanding the Time Convention
The
hourly_tempsarray contains 25 values representing hours 0 through 24:- Index 0: Midnight (00:00) Local Standard Time
- Index 12: Noon (12:00) Local Standard Time
- Index 24: Midnight (00:00) next day — included for continuity
Null Values for Past Hours
The NWS API only provides forecasts for future hours. Hours that have already occurred when the forecast was fetched will have
nullvalues. For example, if a forecast is fetched at 2:00 PM, indices 0-13 will typically benullwhile indices 14-24 will contain forecasted temperatures.When using
mode=history, earlier forecast versions will have more non-null values since they were fetched when more hours were still in the future.Example during Daylight Saving Time
In Austin (CST/CDT), during summer when CDT is in effect:
- Index 0 represents midnight Central Standard Time (which is 1:00 AM CDT civil time)
- The "day" in this API runs from 1:00 AM to 1:00 AM civil time during DST months
cURL Example
curl -G "https://wethr.net/api/v2/nws_forecasts.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KAUS" \ --data-urlencode "date=2025-06-15"Python Example
import requests response = requests.get( 'https://wethr.net/api/v2/nws_forecasts.php', params={'station_code': 'KAUS', 'mode': 'latest'}, headers={'Authorization': f'Bearer {api_key}'} ) data = response.json() print(f"Forecast for {data['forecast_date']} (version {data['version']})") print(f"High: {data['high']}°F / Low: {data['low']}°F") print(f"Hourly: {data['hourly_temps']}")
Push API BETA
⚠️ Beta Product: The Push API is currently in beta. While we strive to maintain high availability and data quality, this service may undergo changes without notice. Additionally, weather data is sourced from upstream providers (NOAA, NWS, Synoptic) and station sensors that may occasionally experience outages or report erroneous values. We strongly recommend implementing your own data validation logic to detect and handle anomalies such as unrealistic temperature spikes, sensor malfunctions, or missing data. Wethr.net is not liable for trading decisions or other actions based on data received through this API.Overview
The Push API delivers real-time weather data as events occur. Instead of polling the REST API, clients maintain a persistent connection and receive updates instantly — including new observations, DSM/CLI releases, and temperature extreme alerts.
Key Benefits:- Real-time updates with sub-second latency
- Efficient — no polling overhead
- Automatic reconnection with event replay
- Temperature alerts for new highs/lows as they occur
Base URL:
https://wethr.net:3443/api/v2/stream
Authentication & Rate Limits
The Push API uses API key authentication. Include your key as a query parameter:
GET https://wethr.net:3443/api/v2/stream?stations=KAUS,KMDW&api_key=YOUR_API_KEYTier Max Stations Connections per User Professional 5 1 Developer Unlimited 1 Enterprise Unlimited 1 Requirements:
- HTTPS only
- One connection per user account
- API key must be enabled in your account
Connecting
Parameters
Parameter Required Description stationsYes Comma-separated station codes (e.g., KAUS,KMDW,KLAX)api_keyYes Your API key last_event_idNo Resume from a specific event ID (for reconnection) Connection Lifecycle
- Client connects with station list and API key
- Server validates credentials and station limits
- Server sends
connectedevent with subscribed stations - Server streams events as they occur
- Server sends periodic
heartbeatevents (every 30s) - On disconnect, client can reconnect with
last_event_idto replay missed events
Event Types
Event Description Frequency connectedConnection established Once on connect heartbeatKeep-alive signal Every 30 seconds observationNew weather observation (METAR/HF-METAR/SPECI) Every 1-5 minutes per station dsmDaily Summary Message released Varies by station cliClimate Report released Varies by station new_highNew temperature high detected As conditions change new_lowNew temperature low detected As conditions change Note — One Minute Observations (OMO): Raw OMO data is not currently available as a Push event type. We anticipate adding OMO events in the coming months. However, thewethr_highandwethr_lowvalues included innew_highandnew_lowevents already incorporate OMO data — if a one minute observation results in a new high or low, these events fire in real time. Thewethr_highandwethr_lowfields withinobservationevents also reflect the current OMO-informed high and low at the time the observation is reported.
Payload Reference
observation
Sent when a new METAR, HF-METAR, or SPECI observation is received. The example below shows all fields that may be present; some fields are only included conditionally (see the field reference for details).
{ "station_code": "KAUS", "event": "observation", "timezone": "America/Chicago", "local_date": "2026-02-08", "product": "HF-METAR", "observation_time_utc": "2026-02-08T18:55:00Z", "temperature_celsius": 24.0, "temperature_fahrenheit": 75.2, "temperature_precision": "decimal", "dew_point_celsius": 12.0, "dew_point_fahrenheit": 53.6, "lowest_probable_f": 75, "highest_probable_f": 75, "lowest_probable_c": 24, "highest_probable_c": 24, "wind_direction": 200, "wind_speed_mph": 14, "wind_speed_kmh": 22, "wind_gust_mph": null, "wind_gust_kmh": null, "visibility_miles": 10.0, "altimeter_hpa": 1019.8, "sea_level_pressure_mb": 1019.6, "precipitation_inches": 0.0, "wethr_high": { "nws": {"value_f": 75, "value_c": 24, "time_utc": "2026-02-08T18:44:00Z"}, "wu": {"value_f": 73, "value_c": 23, "time_utc": "2026-02-08T17:53:00Z"} }, "wethr_low": { "nws": {"value_f": 42, "value_c": 6, "time_utc": "2026-02-08T12:53:00Z"}, "wu": {"value_f": 42, "value_c": 6, "time_utc": "2026-02-08T12:53:00Z"} }, "high_valid_through_utc": "2026-02-08T18:00:00Z", "low_valid_through_utc": "2026-02-08T12:00:00Z", "anomaly": false, "suspect_temperature": null, "data_quality": { "nws": {"ok": true, "warnings": []}, "wu": {"ok": true, "warnings": []} }, "cloud_layers": { "count": 2, "layers": [ {"height_ft_agl": 3500, "coverage": "SCT"}, {"height_ft_agl": 12000, "coverage": "BKN"} ] }, "six_hour_high_celsius": 24.4, "six_hour_high_f": 76, "six_hour_low_celsius": 18.3, "six_hour_low_f": 65, "id": "49500", "timestamp": "2026-02-08T18:56:15Z" }Field Reference — observation
Field Type Always Present Description station_codestring Yes ICAO station identifier (e.g., KAUS)eventstring Yes Always "observation"for this event typetimezonestring Yes IANA timezone identifier for the station (e.g., America/Chicago)local_datestring Yes Current local date at the station in YYYY-MM-DDformatproductstring Yes Source product: METAR,HF-METAR, orSPECIobservation_time_utcstring Yes UTC timestamp of the observation as reported by the station (ISO 8601) temperature_celsiusnumber | null Yes Observed temperature in Celsius (2 decimal places when source is precise, otherwise rounded). nullif rejected — seesuspect_temperature.temperature_fahrenheitnumber | null Yes Observed temperature in Fahrenheit (1 decimal place). nullif rejected.temperature_precisionstring Yes Source precision: "decimal"if the observation reports a 1-decimal value (e.g., HF-METAR with T-group), or"integer"if reported only as a whole degree. Determines whetherlowest_probable/highest_probableare a single value or a range.dew_point_celsiusnumber | null Yes Dew point in Celsius. nullif dew point depression exceeds 12°C (treated as suspect).dew_point_fahrenheitnumber | null Yes Dew point in Fahrenheit lowest_probable_finteger | null Yes Lower bound of the rounding-uncertainty range for the current observation's temperature in °F. When temperature_precisionis"integer", this is the lowest value that could have rounded to the reported temperature. When precision is"decimal", this equalshighest_probable_f. Not the day's low — seewethr_lowfor that.highest_probable_finteger | null Yes Upper bound of the rounding-uncertainty range for the current observation in °F. See lowest_probable_f.lowest_probable_cinteger | null Yes Lower bound of the rounding-uncertainty range in °C highest_probable_cinteger | null Yes Upper bound of the rounding-uncertainty range in °C wind_directioninteger | null Yes Wind direction in degrees (0–360). nullwhen calm or variable.wind_speed_mphinteger | null Yes Sustained wind speed in miles per hour (converted from knots) wind_speed_kmhinteger | null Yes Sustained wind speed in kilometers per hour wind_gust_mphinteger | null Yes Wind gust speed in mph. nullwhen no gust was reported or when the gust value failed validation (e.g., gust less than sustained speed).wind_gust_kmhinteger | null Yes Wind gust speed in km/h visibility_milesnumber | null Yes Visibility in statute miles. Values reported as "10+" in METAR are normalized to 10.0.altimeter_hpanumber | null Yes Altimeter setting in hectopascals (mb-equivalent) sea_level_pressure_mbnumber | null Yes Sea level pressure in millibars (only present in hourly METARs that include the SLP group) precipitation_inchesnumber | null Yes Precipitation amount reported in this observation, in inches. Typically the hourly precipitation group from a METAR. wethr_highobject Yes Day's high temperature computed two ways. See Wethr High/Low below. wethr_lowobject Yes Day's low temperature computed two ways. See Wethr High/Low below. high_valid_through_utcstring | null Yes UTC timestamp through which the wethr_highis considered locked in.nullmeans the high is still based on preliminary observations and may change. See Valid Through below.low_valid_through_utcstring | null Yes UTC timestamp through which the wethr_lowis considered locked inanomalyboolean Yes Set to truewhen an upstream DSM/CLI high looked suspicious compared to surrounding METAR observations. See Data Quality Flags.suspect_temperatureobject | null Yes nullin the normal case. When this observation's temperature failed plausibility checks, this object holds the rejected value and reason, andtemperature_celsius/temperature_fahrenheitarenull. See Data Quality Flags.data_qualityobject Yes Per-resolution-mode report of missing temperature readings ( -999) in the day's window, keyednwsandwu. Each holdsokand awarningsarray. See Data Quality Flags.cloud_layersobject Yes Cloud cover layers reported in the observation. See Cloud Layers below for structure. six_hour_high_celsiusnumber Conditional 6-hour maximum temperature in Celsius. Present only when this observation includes the 6-hour group (typically the :53 hourly METAR every 6 hours: 00z, 06z, 12z, 18z). six_hour_high_finteger Conditional 6-hour maximum in Fahrenheit (rounded). Present when six_hour_high_celsiusis.six_hour_low_celsiusnumber Conditional 6-hour minimum temperature in Celsius. Same presence rules as six_hour_high_celsius.six_hour_low_finteger Conditional 6-hour minimum in Fahrenheit twentyfour_hour_high_celsiusnumber Conditional 24-hour maximum temperature in Celsius. Present only on the 00z and 12z synoptic observations that report the 24-hour group. twentyfour_hour_high_finteger Conditional 24-hour maximum in Fahrenheit twentyfour_hour_low_celsiusnumber Conditional 24-hour minimum temperature in Celsius twentyfour_hour_low_finteger Conditional 24-hour minimum in Fahrenheit idstring Yes Unique monotonic event ID. Use this with last_event_idto replay missed events after reconnection.timestampstring Yes UTC timestamp when Wethr.net published this event (ISO 8601). Note this is the publish time, not the observation time — see observation_time_utc.Wethr High / Low Objects
Both
wethr_highandwethr_lowcontain two parallel calculations of the day's temperature extreme. You should choose the one that matches your settlement methodology.Logic Inputs Time Window nwsAll observations (METAR, SPECI, HF-METAR, OMO) plus official DSM/CLI reports once released Standard Time window (no DST adjustment), matching NWS climate reporting wuMETAR observations only Local clock time window (DST-aware) Each logic block contains
value_f(integer °F),value_c(integer °C), andtime_utc(UTC timestamp when the extreme occurred, ornullif no extreme has been recorded yet today).Valid Through (HVT / LVT)
high_valid_through_utcandlow_valid_through_utcindicate the time through which the high/low values are considered "locked in" based on official reports (DSM/CLI) and confirmed 6-hour extremes. Anullvalue means the extreme is still based on preliminary observations and may change. Once the official daily summary is released, these fields become non-nulland the wethr_high/wethr_low values for the trading day are final.Cloud Layers Object
The
cloud_layersobject summarizes the cloud cover groups reported in the METAR. It always has the following shape:Field Type Description countinteger | null Number of cloud layers reported (0–3). nullif the source did not report a cloud layer count.layersarray Ordered array of layer objects, lowest base first. May be empty (clear skies or no data). Each layer object contains:
Field Type Description height_ft_aglinteger | null Cloud base height in feet above ground level coveragestring | null Coverage code: CLR(clear),FEW(few, 1/8–2/8),SCT(scattered, 3/8–4/8),BKN(broken, 5/8–7/8),OVC(overcast, 8/8). May also beVV(vertical visibility) in obscured conditions.A layer is included whenever either
height_ft_aglorcoverageis non-null, so consumers should not assume both fields are always populated on every layer.Data Quality Flags:
anomaly,suspect_temperature, anddata_qualityhelp identify potentially erroneous or incomplete data. See Data Quality Flags below for details.dsm
Sent when the Daily Summary Message is released by the National Weather Service for this station. Timing varies by station but typically occurs once per day after the climatological day ends.
{ "station_code": "KAUS", "event": "dsm", "timezone": "America/Chicago", "for_date": "2026-02-07", "valid_through_utc": "2026-02-08T06:00:00Z", "high_f": 84, "high_c": 29, "high_time_utc": "2026-02-07T21:53:00Z", "low_f": 42, "low_c": 6, "low_time_utc": "2026-02-07T12:53:00Z", "anomaly": false, "id": "49123", "timestamp": "2026-02-08T06:15:00Z" }Field Reference — dsm
Field Type Description station_codestring ICAO station identifier eventstring Always "dsm"timezonestring IANA timezone identifier for the station for_datestring The climatological date this summary covers, in YYYY-MM-DDformat (local date)valid_through_utcstring UTC timestamp through which these values are considered final/locked in high_finteger | null Daily high temperature in Fahrenheit as reported in the DSM high_cnumber | null Daily high temperature in Celsius (2 decimal places) high_time_utcstring | null UTC timestamp at which the high occurred, when included in the DSM low_finteger | null Daily low temperature in Fahrenheit low_cnumber | null Daily low temperature in Celsius low_time_utcstring | null UTC timestamp at which the low occurred, when included in the DSM anomalyboolean truewhen the DSM high looks suspiciously elevated vs. nearby METAR observations. See Data Quality Flags.idstring Unique monotonic event ID timestampstring UTC publish time cli
Sent when the Climate Report (CLI product) is released for this station. Like the DSM, timing varies by station.
{ "station_code": "KAUS", "event": "cli", "timezone": "America/Chicago", "for_date": "2026-02-07", "valid_through_utc": "2026-02-08T06:00:00Z", "high_f": 84, "high_c": 29, "high_time_utc": "2026-02-07T21:53:00Z", "low_f": 42, "low_c": 6, "low_time_utc": "2026-02-07T12:53:00Z", "anomaly": false, "id": "49456", "timestamp": "2026-02-08T14:30:00Z" }Field Reference — cli
Field Type Description station_codestring ICAO station identifier eventstring Always "cli"timezonestring IANA timezone identifier for the station for_datestring The climatological date this report covers ( YYYY-MM-DDlocal)valid_through_utcstring UTC timestamp through which these values are considered final/locked in high_finteger | null Daily high temperature in Fahrenheit as reported in the CLI high_cnumber | null Daily high temperature in Celsius (2 decimal places) high_time_utcstring | null UTC timestamp at which the high occurred, when included in the CLI low_finteger | null Daily low temperature in Fahrenheit low_cnumber | null Daily low temperature in Celsius low_time_utcstring | null UTC timestamp at which the low occurred, when included in the CLI anomalyboolean truewhen the CLI high looks suspiciously elevated vs. nearby METAR observationsidstring Unique monotonic event ID timestampstring UTC publish time new_high / new_low
Sent immediately when a new temperature extreme is detected for the trading day. These are leaner than
observationevents — they only carry the changed extreme and the value it replaced.{ "station_code": "KAUS", "event": "new_high", "logic": "nws", "value_f": 75, "value_c": 24, "prev_value_f": 73, "prev_value_c": 23, "observation_time_utc": "2026-02-08T18:44:00Z", "id": "49478", "timestamp": "2026-02-08T18:46:29Z" }The
logicfield indicates which calculation triggered the alert (nwsorwu). You may receive separate alerts for each logic if both detect a new extreme — for example, a single observation may produce onenew_highwithlogic: "nws"and a secondnew_highwithlogic: "wu".Field Reference — new_high / new_low
Field Type Description station_codestring ICAO station identifier eventstring Either "new_high"or"new_low"logicstring Which calculation methodology triggered this alert: "nws"or"wu". See Wethr High/Low.value_finteger The new extreme temperature in Fahrenheit value_cinteger The new extreme temperature in Celsius prev_value_finteger | null The prior extreme this replaced, in Fahrenheit. nullif this is the first extreme recorded today.prev_value_cinteger | null The prior extreme this replaced, in Celsius observation_time_utcstring UTC timestamp of the observation that produced the new extreme idstring Unique monotonic event ID timestampstring UTC publish time
Data Quality Flags
The Push API includes flags to help you identify potentially erroneous data. We strongly recommend checking these fields before using observation data for trading or other high-stakes decisions.
Field Event(s) Type Description anomalyobservation,dsm,cliboolean DSM/CLI high temperature is suspiciously elevated vs. nearby METAR observations (possible erroneous official report) suspect_temperatureobservationobject | null METAR temperature failed plausibility checks. When present, temperature_celsius/temperature_fahrenheitarenull. The rejected values and reason are preserved in this object. The implausible reading is excluded from all calculations — it will not affectwethr_high,wethr_low, or triggernew_high/new_lowalerts.data_qualityobservationobject Per-mode ( nws/wu) report of missing temperature readings (the-999sentinel) across the day's window. Each mode hasokand awarningsarray. Unlikesuspect_temperature(one rejected reading), this signals incomplete coverage that can leave the reported high/low understated. See data_quality (Missing Temperature) below.suspect_temperature Object
Field Type Description raw_celsiusnumber The rejected temperature in Celsius raw_fahrenheitnumber The rejected temperature in Fahrenheit reasonstring Machine-readable reason code (currently: out_of_range)messagestring Human-readable description Example: Suspect Temperature Event
{ "station_code": "KLAX", "event": "observation", "product": "METAR", "temperature_celsius": null, "temperature_fahrenheit": null, "suspect_temperature": { "raw_celsius": -61.0, "raw_fahrenheit": -77.8, "reason": "out_of_range", "message": "Temperature outside plausible range (-60°C to 60°C)" }, "wethr_high": { "nws": {"value_f": 68, "value_c": 20, "time_utc": "2026-02-08T21:53:00Z"}, "wu": {"value_f": 68, "value_c": 20, "time_utc": "2026-02-08T21:53:00Z"} }, "anomaly": false, "id": "49501", "timestamp": "2026-02-08T22:56:15Z" }In this example, KLAX reported −61°C which is clearly erroneous. The temperature was rejected and preserved in
suspect_temperaturefor transparency. Thewethr_highandwethr_lowvalues are unaffected, and nonew_lowalert was triggered.data_quality (Missing Temperature) NEW
The
observationevent includes adata_qualityobject that reports gaps in the day's temperature observations — readings the station logged as missing (the-999sentinel). Unlikesuspect_temperature(a single rejected reading), this looks across the whole trading-day window and warns when temperature coverage was incomplete, which can leave the reportedwethr_high/wethr_lowunderstated.It is keyed per resolution mode (
nwsandwu) because each mode scans its own window. Each mode holds an envelope:Field Type Description okboolean truewhen no problems were found (thewarningsarray is empty)warningsarray Zero or more warning objects (below). Currently the only warning is temperature_data_missing; the array allows for additional checks in future without changing the shape.Each warning object:
Field Type Description codestring Stable identifier: "temperature_data_missing"severitystring "warning"or"critical"(see escalation rules below)messagestring Human-readable summary. Treat as display text only — read the structured fields below for logic. missing_countinteger Total number of missing ( -999) temperature readings in the windowlongest_runinteger Length of the longest run of consecutive missing readings longest_run_minutesinteger Duration of that longest run, in minutes first_missing_utcstring UTC time of the first missing reading ( YYYY-MM-DD HH:MM:SS)last_missing_utcstring UTC time of the last missing reading activeboolean truewhen the most recent reading is missing — the station appears to still be downactive_runinteger Length of the current trailing run of missing readings ( 0when not active)near_extremeboolean truewhen a missing reading falls near the reported high or low (i.e.near_highornear_low)near_highboolean truewhen a gap falls within ~60 minutes of the reported high's timenear_lowboolean truewhen a gap falls within ~60 minutes of the reported low's timeSeverity. Any missing reading produces at least a
warning. It escalates tocriticalwhen the station is currently down (active) or when a gap sits near the reported high or low (near_high/near_low) — the case where the reported extreme is most likely to be wrong, since the true high/low could have occurred during the gap.Example: gap near the day's high
"data_quality": { "nws": { "ok": false, "warnings": [ { "code": "temperature_data_missing", "severity": "critical", "message": "2 temperature readings missing (-999) in this window; longest gap 2 consecutive (~10 min) — a gap falls within ~60 min of the day's high, so wethr_high/wethr_low may be unreliable.", "missing_count": 2, "longest_run": 2, "longest_run_minutes": 10, "first_missing_utc": "2026-02-08 20:43:00", "last_missing_utc": "2026-02-08 20:48:00", "active": false, "active_run": 0, "near_extreme": true, "near_high": true, "near_low": false } ] }, "wu": {"ok": true, "warnings": []} }Here the NWS window had a two-reading gap near the time of the day's high, so its high may be understated and the warning is
critical; the WU window was complete. In the REST Observations API the same object appears, keyed by the singlelogicrequested rather than both modes.
Client Examples
cURL — Test Connection
curl -N "https://wethr.net:3443/api/v2/stream?stations=KAUS,KMDW&api_key=YOUR_API_KEY"JavaScript (Browser)
const apiKey = 'YOUR_API_KEY'; const stations = ['KAUS', 'KMDW', 'KLAX']; const url = `https://wethr.net:3443/api/v2/stream?stations=${stations.join(',')}&api_key=${apiKey}`; const eventSource = new EventSource(url); eventSource.addEventListener('observation', (e) => { const data = JSON.parse(e.data); // Check for suspect temperature if (data.suspect_temperature) { console.warn(`⚠️ ${data.station_code}: Suspect temp rejected (${data.suspect_temperature.raw_fahrenheit}°F - ${data.suspect_temperature.reason})`); return; } console.log(`${data.station_code}: ${data.temperature_fahrenheit}°F`); console.log(` Wethr High (NWS): ${data.wethr_high.nws.value_f}°F`); }); eventSource.addEventListener('new_high', (e) => { const data = JSON.parse(e.data); console.log(`🔥 NEW HIGH at ${data.station_code}: ${data.value_f}°F (was ${data.prev_value_f}°F)`); }); eventSource.addEventListener('new_low', (e) => { const data = JSON.parse(e.data); console.log(`❄️ NEW LOW at ${data.station_code}: ${data.value_f}°F (was ${data.prev_value_f}°F)`); }); eventSource.onerror = (e) => { console.error('Connection error:', e); };Python
import sseclient import requests import json api_key = 'YOUR_API_KEY' stations = 'KAUS,KMDW,KLAX' url = f'https://wethr.net:3443/api/v2/stream?stations={stations}&api_key={api_key}' response = requests.get(url, stream=True) client = sseclient.SSEClient(response) for event in client.events(): if event.event == 'observation': data = json.loads(event.data) # Check for suspect temperature if data.get('suspect_temperature'): st = data['suspect_temperature'] print(f"⚠️ {data['station_code']}: Suspect temp rejected ({st['raw_fahrenheit']}°F - {st['reason']})") continue print(f"{data['station_code']}: {data['temperature_fahrenheit']}°F") print(f" Wethr High (NWS): {data['wethr_high']['nws']['value_f']}°F") elif event.event == 'new_high': data = json.loads(event.data) print(f"🔥 NEW HIGH at {data['station_code']}: {data['value_f']}°F")Node.js
const EventSource = require('eventsource'); const apiKey = 'YOUR_API_KEY'; const stations = 'KAUS,KMDW,KLAX'; const url = `https://wethr.net:3443/api/v2/stream?stations=${stations}&api_key=${apiKey}`; const es = new EventSource(url); es.addEventListener('observation', (e) => { const data = JSON.parse(e.data); console.log(`${data.station_code}: ${data.temperature_fahrenheit}°F`); }); es.addEventListener('new_high', (e) => { const data = JSON.parse(e.data); console.log(`NEW HIGH at ${data.station_code}: ${data.value_f}°F`); }); es.onerror = (err) => { console.error('Error:', err); };Reconnection with Event Replay
If your connection drops, you can resume from where you left off using the
last_event_idparameter. Track theidfield from each event and pass it on reconnect:let lastEventId = null; function connect() { let url = `https://wethr.net:3443/api/v2/stream?stations=KAUS&api_key=${apiKey}`; if (lastEventId) { url += `&last_event_id=${lastEventId}`; } const es = new EventSource(url); es.onmessage = (e) => { const data = JSON.parse(e.data); lastEventId = data.id; // Track for reconnection }; es.onerror = () => { es.close(); setTimeout(connect, 5000); // Reconnect after 5s }; } connect();
Model Accuracy API NEW DEV+
GET/api/v2/model_accuracy.php🔒 Developer & Enterprise Only: This endpoint requires a Developer or Enterprise tier API key. Professional and Testing tier keys will receive a403 Access Deniedresponse.Returns per-model forecast accuracy metrics for a station, including MAE (Mean Absolute Error), bias, and RMSE across configurable time windows. Data is computed daily from verified high or low temperature observations compared against raw model output. You can query either the daily high (default) or daily low via the
extremeparameter.All 66 stations (US and international) are supported. Data is updated once daily and cached server-side for performance.
📐 About thebiasfield — read this before integrating:The
biasfield returned by this endpoint is computed asactual − forecast. Mathematically, this value represents the correction factor — the number you would add to the model's forecast to match the average actual on these days.- Positive
bias= the model under-predicted (forecast was lower than what actually occurred). The model "ran cold." - Negative
bias= the model over-predicted (forecast was higher than what actually occurred). The model "ran warm."
If you prefer the conventional bias measure used in most forecast verification literature — where positive means a warm-running model — simply negate the value:
conventional_bias = -bias. Both representations describe the same underlying error in opposite sign conventions. The field name and value are preserved as-is for backward compatibility with existing integrations.Endpoint & Parameters
Parameter Required Description station_codeYes ICAO station identifier (e.g., KORD,EGLC)extremeNo Which daily extreme to query. Values: high(default),low. Determines whether the response describes daily high or daily low temperature accuracy.windowNo Time window for accuracy metrics. Values: 7d(default),14d,30d,90dmodelNo Comma-separated model filter (case-sensitive). Example: GFS,HRRR,ECMWF-IFS. Omit for all models.run_timeNo Comma-separated run time filter. Example: 00z,12z. Omit for all run times.lead_timeNo Comma-separated lead-time bucket filter. Lead time is the gap between when a model run was issued and when the actual extreme occurred. Valid bucket labels: 0-3h,3-6h,6-12h,12-18h,18-24h. Example:0-3h,3-6h. Omit for all buckets.includeNo Comma-separated list of optional data sections to include: trends,daily_detail. See Optional Includes.Available Models:
ARPEGE,ECMWF-IFS,GEFS,GEM-GDPS,GEM-HRDPS,GFS,GFS-MOS,HRRR,ICON,JMA,LAV-MOS,NAM,NAM-MOS,NAM4KM,NBM,NBS-MOS,NWS,RAP,UKMONote: Not all models have data for all run times or all stations. The response will only include models and runs that have available data within the requested window.
Example Requests
# Default: high temperature, all models, 7-day window, core metrics only GET /api/v2/model_accuracy.php?station_code=KORD # Low temperature accuracy GET /api/v2/model_accuracy.php?station_code=KORD&extreme=low # Specific models and window GET /api/v2/model_accuracy.php?station_code=KMIA&window=30d&model=GFS,HRRR,ECMWF-IFS # Filter by run time GET /api/v2/model_accuracy.php?station_code=KDTW&window=14d&model=GFS&run_time=12z,18z # Filter by lead time (only forecasts issued within 6 hours of the actual extreme) GET /api/v2/model_accuracy.php?station_code=KAUS&window=30d&lead_time=0-3h,3-6h # Low temperature with daily detail GET /api/v2/model_accuracy.php?station_code=KAUS&extreme=low&window=7d&include=daily_detail # Include trend data and daily detail GET /api/v2/model_accuracy.php?station_code=KAUS&window=7d&include=trends,daily_detailExample Response (Default — No Includes)
{ "station": "KDTW", "city": "Detroit, MI", "extreme_type": "high", "computed_at": "2026-03-15T02:15:23.286094", "window": "7d", "models": ["HRRR", "GFS", "ECMWF-IFS"], "days_available": 6, "ensemble": { "mae": 3.94, "bias": 3.94, "rmse": 4.77, "n": 6 }, "per_model": { "HRRR": {"mae": 1.79, "bias": 1.61, "rmse": 2.17, "n": 6}, "GFS": {"mae": 3.32, "bias": 2.88, "rmse": 4.19, "n": 6}, "ECMWF-IFS": {"mae": 3.83, "bias": 3.17, "rmse": 4.53, "n": 6} }, "per_model_run": { "HRRR": { "13z": {"mae": 1.77, "bias": 1.17, "rmse": 2.37, "n": 6}, "16z": {"mae": 1.14, "bias": 0.75, "rmse": 1.32, "n": 4}, "18z": {"mae": 0.78, "bias": 0.23, "rmse": 0.82, "n": 4} }, "GFS": { "06z": {"mae": 3.5, "bias": 3.5, "rmse": 4.41, "n": 6}, "12z": {"mae": 3.87, "bias": 3.25, "rmse": 5.11, "n": 5}, "18z": {"mae": 2.85, "bias": 2.85, "rmse": 3.32, "n": 2} }, "ECMWF-IFS": { "06z": {"mae": 4.33, "bias": 4.0, "rmse": 5.4, "n": 6}, "12z": {"mae": 3.71, "bias": 2.57, "rmse": 4.26, "n": 5} } }, "per_model_lead": { "HRRR": { "0-3h": {"mae": 1.21, "bias": 0.45, "rmse": 1.58, "n": 12}, "3-6h": {"mae": 1.46, "bias": 0.83, "rmse": 1.79, "n": 14}, "6-12h": {"mae": 1.92, "bias": 1.34, "rmse": 2.31, "n": 22}, "12-18h": {"mae": 2.41, "bias": 1.97, "rmse": 2.88, "n": 18} }, "GFS": { "0-3h": {"mae": 2.83, "bias": 2.20, "rmse": 3.41, "n": 6}, "3-6h": {"mae": 3.05, "bias": 2.55, "rmse": 3.78, "n": 8}, "6-12h": {"mae": 3.51, "bias": 3.02, "rmse": 4.22, "n": 14}, "12-18h": {"mae": 4.17, "bias": 3.81, "rmse": 4.93, "n": 12} }, "ECMWF-IFS": { "6-12h": {"mae": 3.78, "bias": 3.10, "rmse": 4.45, "n": 7}, "12-18h": {"mae": 4.21, "bias": 3.55, "rmse": 4.89, "n": 9} } } }Note: This example shows a
model=HRRR,GFS,ECMWF-IFSfiltered response. Without the model filter, all 19 tracked models would be returned.Response Fields
Top-Level Fields
Field Type Description stationstring ICAO station code citystring Human-readable city name extreme_typestring Which extreme this response describes: highorlowcomputed_atstring ISO 8601 timestamp of when the accuracy data was last computed windowstring Time window requested ( 7d,14d,30d,90d)modelsarray List of model names included in this response (affected by modelfilter)days_availableinteger Number of days with valid data in this window ensemble
Aggregate accuracy metrics across all models for the requested window.
Field Type Description maedecimal Mean Absolute Error (°F) — average distance between forecast and actual, ignoring direction biasdecimal Mean signed error, computed as actual − forecast. Mathematically equivalent to a correction factor: adding this value to the model's forecast yields the average actual on these days. Positive = forecast was too low, model "ran cold" (under-predicted). Negative = forecast was too high, model "ran warm" (over-predicted). To get the conventional bias measure (positive = warm-running model), negate this value:conventional_bias = -bias.rmsedecimal Root Mean Square Error — like MAE but penalizes large misses more heavily ninteger Number of valid forecast-observation pairs per_model
Object keyed by model name. Each value contains the same
mae,bias,rmse,nfields as the ensemble, but scoped to that specific model. For each day in the window, the model's most recent qualifying run before the actual extreme is used — i.e. the freshest forecast available. Sample sizes here equal the number of days in the window.per_model_run
Object keyed by model name, then by run time label (e.g.,
00z,06z,12z,18z). Each run time entry containsmae,bias,rmse,nfor that specific model + run time combination.Run time labels are in UTC (e.g.,
12z= 12:00 UTC model initialization). Available run times vary by model — for example, HRRR may have13z,16z,18zwhile GFS has06z,12z,18z. Unlikeper_model, every qualifying run is counted here, so sample sizes are typically larger than the per-model table.per_model_lead
Object keyed by model name, then by lead-time bucket label. Lead time is the gap between when a model run was issued and when the actual extreme occurred. This breakdown shows how forecast accuracy varies with how far in advance the prediction was made.
Bucket labels (in order):
0-3h,3-6h,6-12h,12-18h,18-24h. Generally, shorter lead times produce more accurate forecasts. Useful for evaluating how much weight to assign a given model run based on how recently it was issued.Each bucket entry contains
mae,bias,rmse,nfor that specific model + lead-time combination. Likeper_model_run, every qualifying run contributes one data point per bucket — so the same run can appear in this breakdown and the run-time breakdown.
Optional Includes
By default, the response contains only the core metrics (
per_model,per_model_run,per_model_lead,ensemble) for the requested window. Use theincludeparameter to add heavier data sections. All optional includes respect themodelfilter — only data for requested models is returned.trends
Rolling 7-day MAE and bias over time, keyed by model name. Each entry is an array of data points:
{ "trends": { "HRRR": [ {"date": "2025-12-25", "mae_7d": 0.39, "bias_7d": -0.39, "n": 2}, {"date": "2025-12-26", "mae_7d": 0.55, "bias_7d": -0.10, "n": 3}, ... ], "GFS": [ ... ] } }Typically contains ~80 data points per model. Useful for charting model performance over time.
Note on
bias_7d: uses the same convention as thebiasfield (actual − forecast, equivalent to a correction factor). Negate it if you want the conventional bias measure.daily_detail
Last 30 days of actual vs. predicted temperatures for each model, including which run time was used. The actual-temperature field name depends on which extreme you queried:
actual_highwhenextreme=high,actual_lowwhenextreme=low.{ "daily_detail": [ { "date": "2026-03-14", "actual_high": 41, "models": { "HRRR": 41.5, "GFS": 40.5, "ECMWF-IFS": 39.7 }, "run_times": { "HRRR": "18z", "GFS": "18z", "ECMWF-IFS": "12z" } }, ... ] }Payload Size Reference
Approximate response sizes for a typical station (19 models):
Request Size Default (no includes) ~4 KB 2 models filtered, no includes ~400 bytes All includes, no model filter ~120 KB
Client Examples
cURL — Default (7-Day, All Models)
curl -G "https://wethr.net/api/v2/model_accuracy.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KORD"cURL — Filtered by Model and Run Time
curl -G "https://wethr.net/api/v2/model_accuracy.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KDTW" \ --data-urlencode "window=30d" \ --data-urlencode "model=GFS,HRRR,ECMWF-IFS" \ --data-urlencode "run_time=12z,18z"cURL — Low Temperature Accuracy
curl -G "https://wethr.net/api/v2/model_accuracy.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KAUS" \ --data-urlencode "extreme=low" \ --data-urlencode "window=30d"cURL — Filter by Lead Time
curl -G "https://wethr.net/api/v2/model_accuracy.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KORD" \ --data-urlencode "window=30d" \ --data-urlencode "lead_time=0-3h,3-6h"cURL — With Trend Data
curl -G "https://wethr.net/api/v2/model_accuracy.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KAUS" \ --data-urlencode "window=90d" \ --data-urlencode "model=HRRR,NBM" \ --data-urlencode "include=trends"Python — Compare Models Across Windows
import requests api_key = 'YOUR_API_KEY' headers = {'Authorization': f'Bearer {api_key}'} for window in ['7d', '14d', '30d', '90d']: resp = requests.get( 'https://wethr.net/api/v2/model_accuracy.php', params={ 'station_code': 'KORD', 'window': window, 'model': 'GFS,HRRR,ECMWF-IFS' }, headers=headers ) data = resp.json() print(f"\n--- {window} ({data['days_available']} days) ---") for model, stats in data['per_model'].items(): print(f" {model}: MAE={stats['mae']:.1f}° Bias={stats['bias']:+.1f}° (n={stats['n']})")JavaScript — Fetch with Daily Detail
const params = new URLSearchParams({ station_code: 'KMIA', window: '30d', model: 'HRRR,NBM,GFS', include: 'daily_detail' }); fetch(`/api/v2/model_accuracy.php?${params}`, { headers: { 'Authorization': `Bearer ${apiKey}` } }) .then(res => res.json()) .then(data => { console.log(`Station: ${data.station} (${data.city}) — ${data.extreme_type}`); console.log(`Ensemble MAE: ${data.ensemble.mae}°F`); // Show each model's performance for (const [model, stats] of Object.entries(data.per_model)) { console.log(` ${model}: MAE=${stats.mae}° Bias=${stats.bias > 0 ? '+' : ''}${stats.bias}°`); } // The actual-temperature field name depends on extreme_type const actualField = data.extreme_type === 'low' ? 'actual_low' : 'actual_high'; // Show daily predictions vs actual data.daily_detail.forEach(day => { const preds = Object.entries(day.models) .map(([m, p]) => `${m}=${p}°`) .join(', '); console.log(` ${day.date}: Actual=${day[actualField]}° | ${preds}`); }); });
Nearby Stations API NEW BETA DEV+
GET/api/v2/nearby.php🔒 Developer & Enterprise Only: This endpoint requires a Developer or Enterprise tier API key. Professional and Testing tier keys will receive a403 Access Deniedresponse.⚠️ Beta Notice: This API is currently in beta testing. While functional, you may encounter bugs, response shape changes, or unexpected behavior as we iterate. Default parameter values, in particular, may be revised. Please report any issues to help us improve the service.Returns weather stations near a target station along with each station's latest observation. Two operating modes:
- Radius mode (default): given a target station and a radius, returns every station within that radius whose most recent observation is fresher than the
max_age_minutescutoff, sorted by ascending distance. - Explicit list mode: given a comma-separated list of station codes in the
neighborsparameter, returns the latest observation for each (still filtered bymax_age_minutes, still sorted by distance from the target). The radius is ignored in this mode.
The target station's own observation can optionally be embedded in the response. Bearing from the target to each neighbor is included by default. All catalogued stations are eligible — both ICAO stations and mesonet/CWOP sites.
⏱ About the defaultmax_age_minutes=10:The default freshness cutoff is deliberately tight. Many METAR stations report only at the top of the hour, and mesonet stations push at variable intervals — so a 10-minute window will often yield
count: 0even at busy locations. This is correct behavior, not a failure. If you want broader coverage, raise the cutoff (e.g.,max_age_minutes=30for typical use;60or higher for sparse rural areas). The maximum allowed is1440(24 hours), matching the rolling data retention window.Endpoint & Parameters
Parameter Required Description station_codeYes Target station identifier. Must exist in the station catalog with valid coordinates. Examples: KORD,EGLC,KMDW.radius_kmNo Search radius in kilometers. Default 10. Maximum32.2(≈ 20 mi). Ignored whenneighborsis provided.radius_miNo Search radius in miles. Alternative to radius_km. Internally converted (1 mi ≈ 1.60934 km). Maximum20(equivalent to the 32.2 km cap). Ignored whenneighborsis provided.neighborsNo Comma-separated list of station codes to look up directly (max 25). When present, switches the endpoint into explicit list mode — radius/limit are ignored and only the listed stations are returned. Example: KORD,KDPA,KPWK,KGYY.max_age_minutesNo Maximum age in minutes for a station's most recent observation to be eligible. Default 10. Range5–1440. See the note above.limitNo Maximum number of neighbors to return (sorted nearest-first). Default 25. Range1–100. Ignored whenneighborsis provided.include_targetNo If true, the target station's own most recent observation is embedded undertarget.observation. Defaultfalse.include_bearingNo If true(default), each neighbor includesbearing_deg(0–359, true north) andbearing_compass(16-point label). Set tofalseto omit these fields.Distance calculation: a bounding-box prefilter narrows candidates by latitude/longitude indexes, then the haversine formula computes exact great-circle distance in kilometers. Both
distance_kmanddistance_miare always returned. Neighbors are always sorted by ascendingdistance_kmregardless of mode.Caching: identical requests (same target + parameters) are served from a 60-second server-side cache.
Example Requests
# Defaults: 10 km radius, 10 min freshness, up to 25 neighbors, bearing on GET /api/v2/nearby.php?station_code=KMDW # Wider sweep with longer freshness window GET /api/v2/nearby.php?station_code=KMDW&radius_km=50&max_age_minutes=60 # Imperial radius GET /api/v2/nearby.php?station_code=KORD&radius_mi=20&max_age_minutes=30 # Top 5 nearest stations only GET /api/v2/nearby.php?station_code=KATL&radius_km=100&limit=5&max_age_minutes=60 # Include target's own observation GET /api/v2/nearby.php?station_code=KMDW&radius_km=25&include_target=true&max_age_minutes=60 # Explicit list mode — look up specific stations GET /api/v2/nearby.php?station_code=KMDW&neighbors=KORD,KDPA,KPWK,KGYY&max_age_minutes=60Example Response (Radius Mode)
{ "target": { "station_code": "KMDW", "station_name": "Chicago Midway International Airport", "city": "Chicago", "state": "IL", "country": "US", "elevation_m": 188.0, "site_type": "airport", "timezone": "America/Chicago", "latitude": 41.786, "longitude": -87.7524 }, "filters": { "mode": "radius", "radius_km": 25, "radius_mi": 15.53, "max_age_minutes": 60, "limit": 25, "include_target": false, "include_bearing": true, "requested_neighbors": null }, "count": 2, "neighbors": [ { "station_code": "KORD", "station_name": "Chicago O'Hare International Airport", "city": "Chicago O'Hare", "state": "IL", "country": "US", "elevation_m": 201.0, "site_type": "airport", "timezone": "America/Chicago", "latitude": 41.9786, "longitude": -87.9047, "distance_km": 24.4, "distance_mi": 15.16, "age_seconds": 184, "bearing_deg": 323, "bearing_compass": "NW", "observation": { "observation_time": "2026-05-19 04:12:00", "temperature_c": 19.0, "temperature_f": 66.2, "dew_point_c": 13.0, "dew_point_f": 55.4, "previous_temperature_c": 18.9, "previous_temperature_f": 66.02, "wind_direction": "WSW", "wind_speed_kt": 11.0, "wind_speed_mph": 12.66, "wind_speed_mps": 5.66, "wind_speed_kmh": 20.37, "wind_gust_kt": null, "wind_gust_mph": null, "wind_gust_mps": null, "wind_gust_kmh": null, "relative_humidity": 68.42 } }, { "station_code": "KPWK", "...": "(truncated for brevity)" } ] }Response Fields
Top-Level Structure
Field Type Description targetobject Catalog metadata for the target station. Includes observationsub-object only wheninclude_target=true.filtersobject Echo of how the request was resolved (defaults filled in). Useful for confirming what the server actually used. countinteger Number of neighbors returned (zero is valid). neighborsarray Stations matching the query, sorted by ascending distance_km.target
Catalog metadata for the station you queried. Always present (404 returned if the station does not exist or has no coordinates).
Field Type Description station_codestring Echoed station identifier. station_namestring|null Full station name from catalog. citystring|null City. May be null for mesonet/CWOP stations. statestring|null State/region. countrystring|null ISO country (e.g., US).elevation_mdecimal|null Elevation in meters. site_typestring|null e.g. airport,mesonet,cwop.timezonestring|null IANA time zone (e.g., America/Chicago).latitudedecimal Decimal degrees. longitudedecimal Decimal degrees. age_secondsinteger|null Only present when include_target=true. Seconds since the target's most recent observation.observationobject|null Only present when include_target=true. Same shape as a neighbor'sobservation(see below).neighbors[]
Each entry includes catalog metadata, distance/bearing information, and the latest observation.
Field Type Description station_codestring Station identifier. station_namestring|null Full station name. city,state,countrystring|null Catalog location metadata. Mesonet/CWOP stations may have nulls. elevation_mdecimal|null Elevation in meters. site_typestring|null Catalog site type. timezonestring|null IANA time zone. latitude,longitudedecimal Decimal degrees. distance_kmdecimal Great-circle distance to the target, kilometers. distance_midecimal Great-circle distance to the target, miles. bearing_deginteger Compass bearing from target to neighbor, 0–359 degrees from true north. Only present when include_bearing=true(default).bearing_compassstring 16-point compass label ( N,NNE,NE, …). Only present wheninclude_bearing=true.age_secondsinteger Seconds since this neighbor's most recent observation. observationobject The latest observation. See fields below. neighbors[].observation
All temperatures are returned in both Celsius and Fahrenheit. Wind speed and gust are returned in four units (knots, mph, m/s, km/h). Source values are assumed to be stored in knots per METAR/ICAO convention.
Field Type Description observation_timestring UTC timestamp of the observation, YYYY-MM-DD HH:MM:SS.temperature_cdecimal|null Air temperature in Celsius. nullif missing, out of sane range (-60 to 60 °C), or sentinel.temperature_fdecimal|null Air temperature in Fahrenheit. dew_point_cdecimal|null Dew point in Celsius. Nulled if greater than temperature (physically impossible) plus tolerance. dew_point_fdecimal|null Dew point in Fahrenheit. previous_temperature_cdecimal|null The prior observed temperature for this station, Celsius. Useful for delta calculations. previous_temperature_fdecimal|null Same in Fahrenheit. wind_directionstring|null Compass direction (e.g., WSW,NNE) orVRBfor variable.wind_speed_ktdecimal|null Wind speed in knots. wind_speed_mphdecimal|null Wind speed in miles per hour. wind_speed_mpsdecimal|null Wind speed in meters per second. wind_speed_kmhdecimal|null Wind speed in kilometers per hour. wind_gust_ktdecimal|null Peak gust in knots. Nulled if less than steady wind speed (data error). wind_gust_mphdecimal|null Peak gust in miles per hour. wind_gust_mpsdecimal|null Peak gust in meters per second. wind_gust_kmhdecimal|null Peak gust in kilometers per hour. relative_humiditydecimal|null Computed from temperature and dew point using the August-Roche-Magnus formula. Percent (0–100). filters
A faithful echo of how the request resolved. Fields are
nullwhen not applicable (e.g.,radius_kmis null in explicit-list mode).Field Type Description modestring Either radiusorexplicit_list.radius_km,radius_midecimal|null Effective radius (both units). Null in explicit-list mode. max_age_minutesinteger Freshness cutoff applied. limitinteger|null Result cap applied. Null in explicit-list mode. include_targetboolean Whether the target's own observation was embedded. include_bearingboolean Whether bearing fields were included on neighbors. requested_neighborsarray|null The deduplicated, normalized neighbor list. Null in radius mode. Client Examples
cURL — Default Radius Search
curl -G "https://wethr.net/api/v2/nearby.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW"cURL — Wider Radius with Freshness Window
curl -G "https://wethr.net/api/v2/nearby.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KORD" \ --data-urlencode "radius_km=50" \ --data-urlencode "max_age_minutes=60" \ --data-urlencode "limit=10"cURL — Explicit Neighbor List
curl -G "https://wethr.net/api/v2/nearby.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW" \ --data-urlencode "neighbors=KORD,KDPA,KPWK,KGYY" \ --data-urlencode "max_age_minutes=60"Python — Detect Temperature Outliers
import requests import statistics api_key = 'YOUR_API_KEY' headers = {'Authorization': f'Bearer {api_key}'} resp = requests.get( 'https://wethr.net/api/v2/nearby.php', params={ 'station_code': 'KMDW', 'radius_km': 50, 'max_age_minutes': 60, 'include_target': 'true' }, headers=headers ) data = resp.json() # Collect every available temperature in the cluster temps = [] if data['target'].get('observation', {}).get('temperature_f') is not None: temps.append(('KMDW (target)', data['target']['observation']['temperature_f'])) for n in data['neighbors']: t = n.get('observation', {}).get('temperature_f') if t is not None: temps.append((n['station_code'], t)) if len(temps) >= 3: values = [t for _, t in temps] median = statistics.median(values) stdev = statistics.stdev(values) print(f"Cluster median: {median:.1f}°F stdev: {stdev:.2f}") for code, t in temps: flag = " ← outlier" if abs(t - median) > 2 * stdev else "" print(f" {code:8s} {t:5.1f}°F{flag}")JavaScript — Find Closest Reporting Neighbor
const params = new URLSearchParams({ station_code: 'KMDW', radius_km: '50', max_age_minutes: '30', limit: '1' }); fetch(`/api/v2/nearby.php?${params}`, { headers: { 'Authorization': `Bearer ${apiKey}` } }) .then(res => res.json()) .then(data => { if (data.count === 0) { console.log('No nearby station reported recently enough.'); return; } const n = data.neighbors[0]; console.log( `Closest fresh: ${n.station_code} ` + `(${n.city || 'unknown'}) — ` + `${n.distance_km.toFixed(1)} km ${n.bearing_compass}, ` + `${n.age_seconds}s old, ` + `${n.observation.temperature_f}°F, ` + `wind ${n.observation.wind_direction || '?'} ${n.observation.wind_speed_kt || 0} kt` ); });
Pacing API NEW BETA DEV+
GET/api/v2/pacing.php🔒 Developer & Enterprise Only: This endpoint requires a Developer or Enterprise tier API key. Professional and Testing tier keys will receive a403 Forbiddenresponse.⚠️ Beta Notice: This API is currently in beta testing. While functional, you may encounter bugs, response shape changes, or unexpected behavior as we iterate. Default parameter values, in particular, may be revised. Please report any issues to help us improve the service.Returns pacing data for a station's contract day: how the most recent actual observation is running versus each weather model's forecast at the same moment. This is the same calculation shown on the Wethr.net dashboard, computed server-side and exposed as a single key-authenticated call.
For each model, pacing is defined as
actual − model, evaluated at the time of the most recent observation. The model's forecast temperature is linearly interpolated to that exact moment (between the two bracketing forecast points), so the comparison is like-for-like rather than snapped to the nearest forecast hour. A positive pace means actuals are running warmer than the model; a negative pace means cooler.Because a single observation resolves to a range of possible true temperatures, pace is reported as a low/high pair (
pace_low,pace_high) plus a midpoint (pace) and a human-readablepace_string. The range derives from the precision of the underlying METAR reading: a whole-degree Celsius observation carries an implied uncertainty of ±0.5°C, while a tenths-precision (T-group) reading is treated as exact. The endpoint converts that uncertainty band into the station's reporting unit and keeps the integer degrees that fall within it. For a US station, a26°Creading becomes25.5–26.5°C→77.9–79.7°F→lowest_possible: 78,highest_possible: 79. When only one integer degree falls inside the band, the range collapses to that single value (lowest_possible == highest_possible). This matches the range calculation used on the Wethr.net dashboard.📐 Contract-day window & themodeparameter:Pacing is always measured within a single contract day. The
modeparameter controls how that day's boundaries are drawn, mirroring the resolution sources on the dashboard.nws(default) follows Standard Time year-round, shifting the window+1hduring DST so it continues to align with NWS climate-day boundaries (this shift is surfaced asnws_dst_shiftin the response).wundergroundfollows local clock time year-round. Thedate, when omitted, resolves to "today" in the station's own timezone.Endpoint & Parameters
Parameter Required Description station_codeYes Target station identifier. Must exist in the station catalog with a valid timezone. Examples: KTPA,KMDW,EGLC.dateNo Contract day in YYYY-MM-DDformat, interpreted in the station's local time. Defaults to the current day in the station's timezone.modeNo Contract-day window convention: nws(default, Standard Time year-round with a+1hDST shift) orwunderground(local clock time year-round). See the note above.modelsNo Comma-separated list of model names to restrict the response to (max 40). Omitted = all available models. Example: GFS,HRRR,ECMWF-IFS. IncludeNWSin the list to keep the NWS row when filtering.include_nwsNo If true(default), anNWSpacing row is added, computed from the latest NWS hourly forecast. Set tofalseto omit it.Available models:
AROME-HD-15,ARPEGE,ARPEGE-EU,ECMWF-HRES,ECMWF-IFS,GEFS,GEM-GDPS,GEM-HRDPS,GFS,GFS-MOS,HRRR,ICON,JMA,LAV-MOS,NAM,NAM-MOS,NAM4KM,NBM,NBS-MOS,RAP,RRFS,UKMO, plus the syntheticNWSrow. Only models with a forecast run covering the contract day appear in a given response; the model set returned therefore varies by station and day.Reference observation: pacing is anchored to the most recent observation within the contract-day window that is at or before the current time. If no such observation exists yet (e.g., early in the contract day),
reference_observationisnull, every model's pace fields arenull, and a top-levelnoteexplains why. Model run times are still listed in that case.Units: US stations report in Fahrenheit (
unit: "F"); international stations report in their native Celsius (unit: "C"). All temperatures and pace values in a response use the station's native unit.NWS row normalization: the
NWSrow is placed on the same observation-time, full-range basis as every other model so all rows are directly comparable. (The dashboard's NWS display uses a slightly different reference point; this endpoint normalizes it for consistency.)Caching: identical requests (same station + parameters) are served from a 60-second server-side cache.
Example Requests
# Defaults: today in the station's timezone, NWS mode, all models, NWS row on GET /api/v2/pacing.php?station_code=KTPA # Specific contract day GET /api/v2/pacing.php?station_code=KMDW&date=2026-05-29 # Wunderground (local-clock) window convention GET /api/v2/pacing.php?station_code=KMDW&mode=wunderground # Restrict to a few models, drop the NWS row GET /api/v2/pacing.php?station_code=KTPA&models=GFS,HRRR,ECMWF-IFS&include_nws=falseExample Response
{ "station": { "station_code": "KTPA", "station_name": "TAMPA", "city": "Tampa", "state": "FL", "country": "US", "elevation_m": 11, "site_type": null, "timezone": "America/New_York", "latitude": 27.9619, "longitude": -82.5403 }, "contract_day": { "date": "2026-05-29", "mode": "nws", "unit": "F", "is_metric": false, "window_start_utc": "2026-05-29 05:00:00", "window_end_utc": "2026-05-30 05:00:00", "nws_dst_shift": true }, "reference_observation": { "observation_time_utc": "2026-05-29 20:24:00", "observation_time_local": "2026-05-29 16:24:00", "local_hour": 16.4, "temperature": 87.8, "lowest_possible": 87, "highest_possible": 88, "precision_level": null, "unit": "F" }, "filters": { "requested_models": null, "include_nws": true }, "count": 20, "models": { "GFS": { "run_time_utc": "2026-05-29 12:00:00", "model_temp": 89.4, "pace_low": -2.4, "pace_high": -1.4, "pace": -1.9, "pace_string": "-2.4 to -1.4" }, "HRRR": { "run_time_utc": "2026-05-29 19:00:00", "model_temp": 87.5, "pace_low": -0.5, "pace_high": 0.5, "pace": 0, "pace_string": "-0.5 to +0.5" }, "ECMWF-IFS": { "run_time_utc": "2026-05-29 12:00:00", "model_temp": 86.7, "pace_low": 0.3, "pace_high": 1.3, "pace": 0.8, "pace_string": "+0.3 to +1.3" }, "NWS": { "run_time_utc": null, "version": 5, "model_temp": 89, "pace_low": -2, "pace_high": -1, "pace": -1.5, "pace_string": "-2.0 to -1.0" } } }Truncated for brevity — the full
modelsmap contains one entry per available model (here,count: 20). Every model row has the identical shape shown forGFSabove; only theNWSrow differs, carrying aversionfield and anullrun_time_utc.Response Fields
station
Catalog metadata for the requested station.
Field Type Description station_codestring Station identifier. station_namestring Full station name. city,state,countrystring|null Location descriptors. elevation_mdecimal|null Elevation in meters. site_typestring|null Site classification, when available. timezonestring IANA timezone used to resolve the contract day. latitude,longitudedecimal|null Coordinates. contract_day
The window the pacing was computed over.
Field Type Description datestring Contract day ( YYYY-MM-DD), in the station's local time.modestring Window convention applied: nwsorwunderground.unitstring Temperature unit for this response: ForC.is_metricboolean Whether the station reports in metric (Celsius). window_start_utcdatetime Inclusive start of the contract-day window (UTC). window_end_utcdatetime Exclusive end of the contract-day window (UTC). nws_dst_shiftboolean Whether the +1hNWS DST shift was applied to the window.reference_observation
The most recent actual observation that pacing is measured against.
nullwhen no observation is available in the window yet (seenote).Field Type Description observation_time_utcdatetime Observation timestamp (UTC). observation_time_localdatetime Observation timestamp in the station's local time. local_hourdecimal Fractional local hour of the observation (e.g., 16.4= 16:24). This is the moment each model forecast is interpolated to.temperaturedecimal Observed temperature in the station's native unit. lowest_possibleinteger Lowest possible true temperature implied by the reading's precision. Lower bound of the pace range. highest_possibleinteger Highest possible true temperature implied by the reading's precision. Upper bound of the pace range. precision_levelinteger|null Precision indicator for the reading. 1denotes an exact-precision value (lowest_possible == highest_possible);nulldenotes a whole-degree reading resolved to a range.unitstring Unit of the observation: ForC.models
A map keyed by model name. Each entry reports the interpolated model temperature and the resulting pace. When a model has no forecast points within the window, its
model_tempand all pace fields arenull(therun_time_utcis still listed).Field Type Description run_time_utcdatetime|null Model run the forecast came from (UTC). Always nullfor theNWSrow.versioninteger NWS row only. Version of the NWS hourly forecast used. model_tempdecimal|null Model forecast temperature interpolated to the observation's local_hour, in the station's native unit.pace_lowdecimal|null lowest_possible − model_temp.pace_highdecimal|null highest_possible − model_temp.pacedecimal|null Midpoint of pace_lowandpace_high.pace_stringstring|null Human-readable pace, signed (e.g., "+0.3 to +1.3", or a single value when low and high coincide).filters & count
Field Type Description filters.requested_modelsarray|null The normalized model filter applied. nullwhen all models were returned.filters.include_nwsboolean Whether the NWS row was requested. countinteger Number of model rows in models.notestring Present only when no reference observation was available; explains why pace values are null.Client Examples
cURL — Default (All Models, Today)
curl -G "https://wethr.net/api/v2/pacing.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KTPA"cURL — Specific Day, Selected Models
curl -G "https://wethr.net/api/v2/pacing.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW" \ --data-urlencode "date=2026-05-29" \ --data-urlencode "models=GFS,HRRR,ECMWF-IFS" \ --data-urlencode "include_nws=false"Python — Rank Models by How Warm/Cool They Are Running
import requests api_key = 'YOUR_API_KEY' headers = {'Authorization': f'Bearer {api_key}'} resp = requests.get( 'https://wethr.net/api/v2/pacing.php', params={'station_code': 'KTPA'}, headers=headers ) data = resp.json() ref = data.get('reference_observation') if not ref: print(data.get('note', 'No pacing available yet.')) else: unit = data['contract_day']['unit'] print(f"Reference: {ref['temperature']}°{unit} " f"at {ref['observation_time_local']} " f"(range {ref['lowest_possible']}–{ref['highest_possible']})") # Rank by midpoint pace: most positive = actuals warmest vs model rows = [(name, m['pace']) for name, m in data['models'].items() if m.get('pace') is not None] rows.sort(key=lambda r: r[1], reverse=True) print("\nModels running COOLEST vs actuals (actuals warmer) at top:") for name, pace in rows: sign = '+' if pace >= 0 else '' print(f" {name:12s} {sign}{pace:.1f}°{unit}")JavaScript — Show the Consensus Pace
const params = new URLSearchParams({ station_code: 'KTPA' }); fetch(`/api/v2/pacing.php?${params}`, { headers: { 'Authorization': `Bearer ${apiKey}` } }) .then(res => res.json()) .then(data => { if (!data.reference_observation) { console.log(data.note || 'No pacing available yet.'); return; } const unit = data.contract_day.unit; const paces = Object.values(data.models) .map(m => m.pace) .filter(p => p !== null); if (paces.length === 0) { console.log('No model paces to summarize.'); return; } const avg = paces.reduce((a, b) => a + b, 0) / paces.length; const verdict = avg > 0 ? 'WARMER than models' : avg < 0 ? 'COOLER than models' : 'on pace with models'; console.log( `${data.station.station_code}: actuals running ` + `${avg.toFixed(1)}°${unit} ${verdict} ` + `(consensus of ${paces.length} models)` ); }); - Data Quality Warnings NEW: The
-
⚠️ API Version 1 (Deprecated) — This version is deprecated and frozen: it will not receive new fields, data, or features. It remains available and operational so existing integrations keep working and is not being taken down. New development should use API v2, which is actively maintained and offers expanded features, additional data fields, and broader station support. Base URL:
https://wethr.net/api/v1/Introduction Deprecated
The Wethr.net API v1 provides access to weather observation data and model forecasts. This version supports 24 US stations with all temperatures in Fahrenheit.
Base URL:
https://wethr.net/api/v1/
Authentication Deprecated
Include your API key in the request header:
Authorization: Bearer <YOUR_API_KEY>Or:
X-API-Key: <YOUR_API_KEY>
Rate Limiting Deprecated
Tier Requests/Minute Requests/Day Professional 60 5,000 Developer 300 50,000 Enterprise 300 50,000
Error Responses Deprecated
- 400 Bad Request — Invalid parameters
- 401 Unauthorized — Invalid API key
- 429 Too Many Requests — Rate limit exceeded
- 500 Server Error — Internal error
Observations API Deprecated
GET/api/v1/observations.phpMode: Latest Observation
Returns the most recent observation.
GET /api/v1/observations.php?station_code=KMDW&mode=latestExample Response
{ "station_code": "KMDW", "temperature": 26.7, "observation_time": "2025-06-15T18:53:00Z", "lowest_probable_f": 80, "highest_probable_f": 80, "dsm_high_f": 83, "relative_humidity": 55.2 }
Mode: Wethr High
Returns the calculated trading-day high. Logic is determined automatically by station type.
GET /api/v1/observations.php?station_code=KMDW&mode=wethr_highExample Response
{ "station_code": "KMDW", "date": "2025-06-15", "wethr_high": 83, "time_of_high_utc": "2025-06-15 20:53:00", "calculation_logic": "standard" }
Mode: History
Retrieves observations over a time range (max 24 hours).
GET /api/v1/observations.php?station_code=KMDW&start_time=2025-06-15T00:00:00Z&end_time=2025-06-15T12:00:00Z
Observation Data Model
Field Type Description station_codestring Station identifier observation_timedatetime UTC timestamp temperaturedecimal Temperature (Celsius) temperature_fdecimal Temperature (Fahrenheit) lowest_probable_finteger Min probable temp (°F) highest_probable_finteger Max probable temp (°F) dew_pointdecimal Dew point (Celsius) relative_humiditydecimal Relative humidity (%) cli_high/cli_high_fdecimal/int CLI report high dsm_high/dsm_high_fdecimal/int DSM report high six_hour_highdecimal 6-hour high (Celsius) wind_speeddecimal Wind speed (knots) visibilitydecimal Visibility (miles)
Forecasts API Deprecated
GET/api/v1/forecasts.phpParameters
Parameter Required Description location_nameYes Location identifier start_valid_timeYes Start of range (ISO 8601) end_valid_timeYes End of range (ISO 8601) modelNo Model filter Available Models:
ARPEGE,ECMWF-IFS,GEFS,GEM-GDPS,GEM-HRDPS,GFS,GFS-MOS,HRRR,ICON,JMA,LAV-MOS,NAM,NAM-MOS,NAM4KM,NBM,NBS-MOS,RAP,UKMOExample Request
GET /api/v1/forecasts.php?location_name=KMDW&start_valid_time=2025-06-15T18:00:00Z&end_valid_time=2025-06-15T20:00:00Z&model=HRRRForecast Data Model
Field Type Description modelstring Model name location_namestring Location run_timedatetime Model run time valid_timedatetime Forecast valid time forecast_hourinteger Lead time (hours) temperature_kdecimal Temp (Kelvin) temperature_fdecimal Temp (Fahrenheit) temperature_cdecimal Temp (Celsius)
Client Examples Deprecated
cURL — Latest Observation
curl -G "https://wethr.net/api/v1/observations.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW" \ --data-urlencode "mode=latest"cURL — Wethr High
curl -G "https://wethr.net/api/v1/observations.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW" \ --data-urlencode "mode=wethr_high"cURL — History
curl -G "https://wethr.net/api/v1/observations.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "station_code=KMDW" \ --data-urlencode "start_time=2025-06-15T00:00:00Z" \ --data-urlencode "end_time=2025-06-15T12:00:00Z"cURL — Forecasts
curl -G "https://wethr.net/api/v1/forecasts.php" \ -H "Authorization: Bearer <YOUR_API_KEY>" \ --data-urlencode "location_name=KMDW" \ --data-urlencode "start_valid_time=2025-06-15T18:00:00Z" \ --data-urlencode "end_valid_time=2025-06-15T20:00:00Z" \ --data-urlencode "model=HRRR"