Community. Driven. Weather. Data. | Always Ad-Free | Developer FriendlyChecking statusDiscord
Become a supporter
Sign in
API

Forecast API

Every field the models publish for a point, NBM to about eleven days and the HRRR nowcast, with optional severe weather parameters and upper air.


Base URL: https://api.wxalerts.org

The full forecast for a point: the National Blend of Models hour by hour, and the HRRR nowcast beside it, with the model run behind every number. GET /weather is the same data reduced for a screen; this is the unreduced version.

GET /forecast?lat=35.2&lon=-97.4&units=si
GET /forecast?q=norman+ok&units=us
GET /forecast?lat=35.2&lon=-97.4&units=si&include=severe,upper_air

No key needed. Browsers block calls from other websites (CORS allows only wxalerts.org), so call it from a server, script or app. The public rate limit applies. The data changes hourly and responses carry Cache-Control: max-age=300, so cache.

parameter
lat, lon the point
q instead of lat/lon: a place name, ZIP or state (1 to 120 characters). The best match is used; no match is a 404
location_id instead of lat/lon: a place id. Unknown is a 404
units si, metric, us, uk or auto (default). See Units
include comma separated: severe, upper_air. Anything else is a 422
explain true also returns facts, the derived facts the narrative was written from. Default false

With none of lat/lon, q or location_id the answer is a 422.

Two models, never blended

resolution horizon updated carries
hourly, daily NBM, about 10 km about 11 days hourly the forecast: temperature, wind, sky, precipitation and its probabilities
nowcast, current HRRR, about 6 km 18 hours hourly the near term: radar reflectivity, pressure, and on request the severe and upper air fields below

They are kept apart on purpose. When they disagree about the timing of rain, that disagreement is information, and a blend would hide which model said what. models names each run, its age and how far it reaches.

Both series start at the hour in progress. hourly is one step an hour for about the first two days, then every 3 hours, then every 6 hours past about day 8. current is the nowcast step nearest to now, not simply the first.

What a response holds

{
  "server_time": "2026-11-09T19:07:20Z",
  "location": {
    "name": "Norman", "display_name": "Norman, OK", "state": "OK",
    "lat": 35.2, "lon": -97.4, "timezone": "America/Chicago",
    "approximate": false, "distance_km": 6.7
  },
  "cell": { "cell_id": 81168, "domain": "co", "lat": 35.196527, "lon": -97.378552, "resolution_km": 10.2 },
  "models": {
    "nbm": { "resolution_km": 10.2, "cycle": "2026-11-09T18:00:00Z", "age_seconds": 4040, "steps": 104,
             "first_valid": "2026-11-09T19:00:00Z", "last_valid": "2026-11-20T12:00:00Z", "horizon_hours": 256.9 },
    "hrrr": { "resolution_km": 6.0, "cycle": "2026-11-09T18:00:00Z", "age_seconds": 4040, "steps": 17,
              "first_valid": "2026-11-09T19:00:00Z", "last_valid": "2026-11-10T11:00:00Z", "horizon_hours": 15.9 }
  },
  "units": {
    "system": "si", "source": "explicit", "country": null,
    "hourly": { "temperature": { "unit": "K", "label": "K", "description": "2 m air temperature." }, "...": "..." },
    "nowcast": { "pressure": { "unit": "Pa", "label": "Pa", "description": "Mean sea level pressure. ..." }, "...": "..." }
  },
  "coverage": "full",
  "current": { "valid_time": "2026-11-09T19:00:00Z", "...": "..." },
  "nowcast": [
    {
      "valid_time": "2026-11-09T19:00:00Z", "local_time": "2026-11-09T13:00:00-06:00",
      "utc_offset_seconds": -21600, "forecast_hour": 1,
      "wind_direction_cardinal": "SW", "visibility_unrestricted": null,
      "temperature": 293.9, "dewpoint": 283.7, "wind_speed": 4.783, "wind_direction": 218.0,
      "precip_amount": 0.0, "reflectivity": -10.0, "cape": 0.0,
      "pressure": 102000.0, "surface_pressure": 97920.0
    },
    "..."
  ],
  "hourly": [
    {
      "valid_time": "2026-11-09T19:00:00Z", "local_time": "2026-11-09T13:00:00-06:00",
      "utc_offset_seconds": -21600, "forecast_hour": 2,
      "wind_direction_cardinal": "SSW", "visibility_unrestricted": true,
      "temperature": 293.2, "dewpoint": 285.1, "relative_humidity": 59.6,
      "wind_speed": 3.931, "wind_gust": 6.9, "wind_direction": 206.0,
      "cloud_cover": 2.9, "visibility": 16210.0, "ceiling": null,
      "precip_amount": 0.0, "precip_probability": 0.0,
      "rain_probability": 0.0, "snow_probability": 0.0,
      "freezing_rain_probability": 0.0, "ice_pellet_probability": 0.0,
      "thunder_probability": 0.0, "severe_probability": 0.0, "cape": 0.0,
      "sky": "Clear", "precip_type": null, "icon": "clear-day", "is_day": true
    },
    "..."
  ],
  "daily": [
    {
      "date": "2026-11-09", "hours": 11,
      "temperature_min": 285.8, "temperature_max": 294.1,
      "dewpoint_min": 284.6, "dewpoint_max": 285.8,
      "relative_humidity_min": 54.4, "relative_humidity_max": 99.6,
      "wind_speed_max": 3.931, "wind_gust_max": 7.1,
      "wind_direction_prevailing": 198.0, "wind_direction_cardinal": "SSW",
      "cloud_cover_mean": 41.6, "visibility_min": 16210.0, "ceiling_min": 3960.0,
      "precip_amount_total": 0.0, "precip_probability_max": 1.0,
      "rain_probability_max": 0.0, "snow_probability_max": 0.0,
      "freezing_rain_probability_max": 0.0, "ice_pellet_probability_max": 0.0,
      "thunder_probability_max": 0.0, "severe_probability_max": 0.0, "cape_max": 0.0,
      "sky": "Partly cloudy", "precip_type": null, "icon": "partly-cloudy-day"
    },
    "..."
  ],
  "narrative": {
    "now": { "headline": "A comfortable afternoon, clear", "body": ["..."], "hazard": false },
    "next_36h": { "headline": "Quiet through the next day and a half", "body": ["..."], "hazard": false },
    "week": { "headline": null, "body": ["..."], "hazard": false }
  },
  "facts": null
}
  • location is the place the forecast is for. Given lat/lon, it is the nearest named place: approximate: true means the point lies outside that place (say “near Norman”), and distance_km is how far its centre is. Where nothing is close enough to name, name, display_name and state are null. Given q or location_id, it is that place and its coordinates, with distance_km: null. timezone is the IANA zone the local times are in.
  • cell is the NBM grid cell the point fell in. lat/lon here are its centre, not your point. domain is co (the lower 48), ak, hi, pr or gu.
  • models: cycle is when the run started and age_seconds how long ago that was, which is where staleness lives. horizon_hours is from now to last_valid. hrrr is null outside the HRRR grid.
  • Every step carries valid_time (UTC), local_time and utc_offset_seconds for the point’s zone, forecast_hour, wind_direction_cardinal (16 point), and visibility_unrestricted: true when visibility is at the top of the model’s range (16 km, about 10 mi), so you can write “10+ mi”; null on the nowcast, which has no visibility. forecast_hour counts from the run that produced that step. Most steps come from the newest run, but where it skipped an hour an earlier run’s step stays, so order by valid_time, never by forecast_hour.
  • hourly and daily also carry sky, precip_type and icon, decided on the server exactly as on /weather; hourly steps add is_day. A day’s icon is always the daytime one.
  • daily rolls hourly into local calendar days. Each field becomes _min/_max (temperature, dewpoint, humidity), _max (wind, gust, probabilities, cape), _min (visibility, ceiling), _mean (cloud cover), _total (precipitation) or _prevailing (wind direction, a vector mean). hours is how many steps the day was built from: today and the last day are partial, and days past the hourly range have fewer steps by design.
  • Nulls. ceiling: null means no ceiling (unlimited). reflectivity of -10 dBZ is the model’s floor and means clear air; null there only means no value is stored for that hour.

Coverage

coverage says what you got:

coverage where what is filled
full the lower 48 and its surroundings everything
nbm_only Alaska, Hawaii, Puerto Rico, Guam hourly and daily; nowcast is empty, current and models.hrrr are null
outside_domain anywhere else every series empty, cell.cell_id is -1

A field a domain does not publish is left out of its steps and of the units block entirely, rather than sent as null. Hawaii, for example, has no precipitation type or severe_probability.

Units

units is si (exactly what the model published: K, m/s, Pa, kg/m²), metric, us, uk, or auto (the default) from your country. Every response carries a units block naming the unit, a display label and a description of every field in hourly and nowcast. Read it rather than assuming. units.source is explicit when you named a system, geo when it came from your country, or default.

auto varies by caller, so it is sent Cache-Control: private with Vary: CF-IPCountry. Pass a system explicitly for a response a CDN can cache (public, max-age=300).

Probabilities (NBM, hourly)

field meaning
precip_probability chance of more than 0.01 in (0.254 mm) in the hour
thunder_probability chance of a thunderstorm. Not published for Guam
rain_probability, snow_probability, freezing_rain_probability, ice_pellet_probability chance the precipitation is each type. Four independent numbers, not a split of 100: an uncertain hour can be plausibly both rain and snow. CONUS and Alaska only.
severe_probability chance the Craven-Wiedenfeld severe parameter exceeds 65, a significant severe signal. CONUS only.

All are percentages. Probabilities are the highest value anywhere in the 10 km cell, so they answer “anywhere near here”, not “on average across it”.

include=severe

Added to each nowcast step (and so to current). From the HRRR at native resolution, averaged to the 6 km cell; helicity and updraft helicity keep the cell’s peak instead. Surface based cape is in every nowcast step already.

field unit (si) meaning
cin J/kg surface based convective inhibition. Zero or negative; more negative is a stronger cap
mlcape, mlcin J/kg mixed layer, the lowest 90 hPa
mucape, mucin J/kg most unstable parcel in the lowest 255 hPa
cape_0_3km J/kg low-level CAPE
lcl_height m above ground lifted condensation level of a surface parcel
lifted_index K negative is unstable
precipitable_water kg/m² (mm)
srh_0_1km, srh_0_3km m²/s² storm-relative helicity, Bunkers right mover
storm_motion_speed, storm_motion_direction m/s, degrees HRRR’s Bunkers right mover, direction FROM
bulk_shear_0_1km, bulk_shear_0_6km, bulk_shear_0_6km_direction m/s, degrees bulk wind difference
updraft_helicity m²/s² hourly maximum 2 to 5 km; 0 at the analysis hour

CAPE, CIN and helicity stay in J/kg and m²/s² in every system.

include=upper_air

Temperature, dewpoint and wind at 925, 850, 700 and 500 hPa, heights at 850 to 500 hPa, the 250 hPa jet, the 80 m wind and the boundary layer height: temperature_850mb, dewpoint_850mb, height_850mb, wind_speed_850mb, wind_direction_850mb and so on, plus wind_speed_250mb, wind_direction_250mb, wind_speed_80m, wind_direction_80m and boundary_layer_height (above ground).

A level below the ground at that point (850 hPa over Denver, say) comes back null: the model extrapolates under terrain and that number describes no air.

Winds aloft, the 80 m wind, shear and storm motion are in knots in every system but si, because that is how soundings and hodographs read. Heights of pressure levels stay in metres in every system.

All directions are degrees from true north, reported as the direction a wind blows FROM. HRRR publishes its vectors relative to its own grid; they are turned to true north before they are stored.

Narrative and explain

narrative is the forecast in sentences, in three blocks: now, next_36h and week, each a headline (or null) and body paragraphs. It is written by rules from the numbers in the same response, so it never states anything the series do not. An empty body means nothing was worth saying. Outside every model domain narrative is null, unless an alert covers the point, in which case only the alert is said. When an active alert covers the point it leads, and that block’s hazard is true: style the lead paragraph as a warning on that flag, not by matching words.

explain=true fills facts with the derived values the sentences were chosen from (high today, precipitation onset, pressure trend and so on). It is for seeing why a sentence appeared, not a stable interface: its keys can change without notice.

May we use Microsoft Clarity to see how pages are used (clicks, scrolling and replays of a visit, with anything you type hidden), plus a random ID linking that to crash reports? Only to find bugs and confusing screens. Never advertising, never sold.