Skip to content

@mostlyrightmd/weather

  • FetchWithRetryOptions

optional area: string

Two-letter state / marine area code, e.g. "TX".

optional event: string

NWS event name, e.g. "Hurricane Warning". Live route only — the archive serves VTEC codes and no event name, so combining this with source: "iem" throws ContractError naming phenomena/significance as the replacement rather than filtering every row away.

optional fromTime: string

Window start, ISO-8601 with a Z offset.

optional limit: number

Page size for the live route.

optional phenomena: string

VTEC phenomena code, e.g. "HU". Both routes.

optional significance: string

VTEC significance code, e.g. "W". Both routes.

optional source: AlertsSource

Which route answers. Default "nws" (the live CAP feed).

optional toTime: string

Window end, ISO-8601 with a Z offset.

optional vintages: Vintages

Row selector. Default "all". See Vintages.

optional wfo: string

Forecast office, e.g. "MLB". Both routes: it narrows the archive query server-side, and filters the decomposed wfo column on the live route.

optional zone: string

UGC zone/county code, e.g. "TXZ213".


  • FetchWithRetryOptions

optional filename: string

Read one exact upstream file name, bypassing revision selection.

optional revision: string

Read a named reanalysis vintage, as YYYY-MM-DD. HURDAT2 is re-analysed annually and several revisions of the same season coexist, so pinning this is what keeps a point-in-time research read from “predicting” an old storm with values that did not exist until years later. Unset reads the newest revision.

optional vintages: Vintages

Row selector. Default "all". See Vintages.


One HURDAT2 reanalysis file.

bestTrackRevisions() enumerates files rather than rows, which is why it is the one verb in this family that does not return a DataResult: there is no row grain and no schema id to stamp. Pass a filename or a revision back into bestTrack() to read one.

Ordered ascending by revision, never by file name — the two orders disagree, because the date encoding in the name is six digits in some years and eight in others.

basin: "atl" | "nepac"

The file this revision belongs to: "atl" or "nepac". Pass it straight back to BestTrackRevisionsOptions.basin. Not the storm-id vocabulary — the nepac file holds both EP and CP storms.

filename: string

The exact upstream file name — part of the vintage identity.

revision: string

Revision date parsed out of the file name, as YYYY-MM-DD.


  • FetchWithRetryOptions

optional basin: "atl" | "nepac"

Which published file to list: "atl" (Atlantic) or "nepac" (north-east Pacific, which holds both the EP and CP storm-id prefixes). Default "atl", matching Python’s best_track_revisions(basin="atl").

This is the file vocabulary, not the storm-id vocabulary: the nepac file holds two storm-id prefixes, so the two cannot be the same word. An unrecognised value throws ContractError before any request.


One station-local day’s rollup.

Field names mirror Python DailyExtreme TypedDict exactly so cross-language code reads the same way:

  • date, station, low_f, high_f, mean_f, precip_in
  • low_coverage (boolean) and observation_count (int) for debug-friendly gating

date: string

Station-local calendar date as YYYY-MM-DD.

high_c: null | number

Maximum temperature in °C, or null on low coverage.

high_f: null | number

Maximum temperature in °F, or null on low coverage.

low_c: null | number

Minimum temperature in °C, or null on low coverage. This is the settlement-correct quantity Polymarket resolves on (the Python daily_extremes label is Celsius, daily_extremes_low_c).

low_coverage: boolean

True when observation_count < 12 (matches Python low-coverage gate).

low_f: null | number

Minimum temperature in °F, or null on low coverage.

mean_c: null | number

Mean temperature in °C, or null on low coverage.

mean_f: null | number

Mean temperature in °F, or null on low coverage.

observation_count: number

Count of observation rows that contributed to the day.

precip_in: null | number

Total 1-hour precipitation across the local day, in inches.

source_high: null | string

Source identifier of the row that produced tmax (null on low coverage).

source_low: null | string

Source identifier of the row that produced tmin (null on low coverage).

station: string

ICAO station code.


optional merge: DailyExtremesMergeMode

Source merge mode; default "live_v1".

optional signal: AbortSignal

Caller abort signal, propagated to every IEM/AWC fetch (and composed with each attempt’s retry backoff wait). An abort mid-flight rejects promptly with the platform AbortError — never reclassified as a transport error.


high_temp_f: null | number

Daily high °F, rounded to int. null when missing or out-of-bounds.

issued_at: null | string

ISO 8601 UTC issuance time parsed from product[:12], else null.

low_temp_f: null | number

Daily low °F, rounded to int. null when missing or out-of-bounds.

observation_date: string

Local climate day, YYYY-MM-DD.

product_id: null | string

Raw NWS product identifier when present.

report_type: ReportType

Inferred report type.

report_type_priority: number

Numeric priority for dedup (final=3, ncei_final=2.5, correction=2, preliminary=1, estimated=0). Sourced from CLIMATE_REPORT_TYPE_PRIORITY in @mostlyrightmd/core codegen.

source: "iem"

Always "iem" for CLI records.

station_code: string

Station code (3-letter NWS or 4-letter ICAO, caller’s choice).


Optional knobs for forecastNwp.

readonly optional cycle: string

Model run datetime — UTC ISO string.

readonly optional forecastHour: number

Forecast hour ahead of cycle.

readonly optional member: string

Ensemble member id (e.g. GEFS "p05", CFS "03"). Only meaningful for GEFS and CFS.


Options for gauges().

EXACTLY ONE of bbox or gaugeId is required. The two scopes answer differently when there is nothing to return — an empty box is an empty result, an unknown identifier throws — so which one was meant is never inferred, and passing both or neither throws before any request.

  • FetchWithRetryOptions

optional bbox: GaugeBbox

Enumerate the gauge INVENTORY inside this box. Split into tiles no wider than maxTileDegrees on either axis, because the route serves no paging and its unfiltered answer was measured at 13 MB and a gateway timeout. An inverted, zero-width, or off-the-globe box throws before any request.

optional gaugeId: string

Return the ONE detail row for a single gauge: an NWS Location ID ("ABBG1") or a bare USGS site number ("02215260"). The route accepts both on the same path and was measured answering identically, which is why there is one option rather than two. The value is echoed back into the row’s gauge_id column, so a flood frame joins on the identifier the caller already holds.

optional maxTileDegrees: number

Widest tile a single request covers, in the units of srid.

optional srid: string

Spatial reference of the bbox corners. Always sent: the route answers a missing or unrecognised projection with HTTP 200 and an EMPTY gauge list, which reads as “no gauges here”. Default "EPSG_4326".


alert_id: string

primary key, unique per issuance. Live rows carry the CAP properties.id (‘urn:oid:…’); archive rows carry the deterministic ‘iem:{product_id}:{ugc_zone}’ so the two legs never collide and the archive leg is reproducible

optional area_description: null | string

CAP areaDesc: the human-readable zone list

captured_at: string

PROVENANCE STAMP: when the SDK captured this row. Not an event time, which is why it carries no _at_utc suffix. The vintage dedup key, so it is never null

optional certainty: null | string

optional effective_at_utc: null | string

CAP effective: when the alert takes force. Archive leg: utc_issue

optional ends_at_utc: null | string

CAP ends: when the hazard itself ends. Archive leg: utc_init_expire

optional event: null | string

NWS event name (‘Hurricane Warning’); one of 111 in /alerts/types

optional event_tracking_number: null | number

VTEC ETN; unique per (office, phenomena, significance, vtec_year)

optional expires_at_utc: null | string

CAP expires: when this issuance stops being current. Archive leg: utc_expire

optional headline: null | string

optional message_type: null | string

CAP messageType: Alert | Update | Cancel | Ack | Error

optional onset_at_utc: null | string

EVENT TIME: when the hazard itself begins. Archive leg: null (the watchwarn archive serves no separate onset)

optional phenomena: null | string

VTEC phenomena code (HU = hurricane, FF = flash flood, FW = fire weather)

optional product_id: null | string

AFOS product id (‘202410081215-KMLB-WHUS72-MWWMLB’); joins to the text product

optional raw_cap: null | string

verbatim CAP ‘properties’ object as JSON, for re-parse provenance. Populated on nws.alerts.live rows only

optional raw_csv: null | string

verbatim IEM watchwarn CSV record line, for re-parse provenance. Populated on iem.vtec.archive rows only

optional references: null | string

JSON array of the alert_id values this issuance supersedes, in served order — a supersession EDGE, never a delete. Null when this is the first issuance of the event

optional sender_name: null | string

optional sent_at_utc: null | string

KNOWLEDGE TIME: when NWS issued this alert — the instant it became knowable. align() cuts on this column. Archive leg: utc_prodissue

optional severity: null | string

optional significance: null | string

VTEC significance code (W = warning, A = watch, Y = advisory, S = statement)

source: string

per-row source identity == df.attrs[‘source’]

ugc_zone: string

UGC zone/county code (FLC086); the second half of the row identity

optional urgency: null | string

optional vtec_action: null | string

VTEC action: NEW | CON | CAN | EXP | EXT | UPG — the supersession verb

optional vtec_class: null | string

VTEC product class: O (operational) | T (test) | E (exercise) | X

optional vtec_office: null | string

VTEC 4-letter office id (KPSR); the archive’s 3-letter wfo drops the K/P

optional vtec_string: null | string

the verbatim VTEC string, e.g. ‘/O.CON.KPSR.XH.W.0007.000000T0000Z-260804T0300Z/’. Kept beside the decomposed columns so a re-parse is always possible

optional vtec_year: null | number

year the ETN sequence belongs to; the archive returns it as a column

optional wfo: null | string

3-letter forecast office (PSR); the archive leg’s own spelling


basin: string

AL | EP | CP, from the storm_id prefix

captured_at: string

PROVENANCE STAMP: when the SDK captured this row (the vintage dedup key)

hurdat2_filename: string

the exact upstream filename (‘hurdat2-1851-2025-02272026.txt’); there is no stable ‘latest’ alias, so the filename is part of the vintage identity

hurdat2_revision: string

KNOWLEDGE TIME: the reanalysis vintage date parsed out of the HURDAT2 filename — the date these values became knowable, NOT the date of the fix

optional landfall_flag: null | string

HURDAT2 record identifier; ‘L’ marks a LANDFALL fix — the settlement-relevant flag for ‘hurricane hits X’ contracts

latitude: number

units: degrees_north

longitude: number

units: degrees_east

optional max_wind_kt: null | number

units: kt — HURDAT2 serves -999 for missing; the parser maps that to null

optional min_pressure_mb: null | number

units: hPa — HURDAT2 serves -999 for missing; the parser maps that to null

optional radius_max_wind_nm: null | number

units: nmi

raw_hurdat2: string

verbatim HURDAT2 record line, for re-parse provenance

source: string

per-row source identity == df.attrs[‘source’]

status: string

system status: TD | TS | HU | EX | SD | SS | LO | WV | DB

storm_id: string

HURDAT2 header id (AL142024); the per-entity join key

optional storm_name: null | string

valid_at_utc: string

EVENT TIME: the synoptic fix time (HURDAT2 YYYYMMDD + HHMM)

optional wind_radii_nm: null | string

units: nmi — the 12 quadrant wind-radii fields as a JSON array string in HURDAT2 order; missing values are JSON null


optional action_flow_cfs: null | number

units: cfs — cubic feet per second, the unit this route already serves. The stage-and-flow route serves KILO-cubic-feet per second for the same gauge, so only that product’s flow_cfs column is comparable to this one

optional action_stage_ft: null | number

units: ft — NULL when the upstream serves its no-threshold encoding

captured_at: string

PROVENANCE STAMP: when the SDK captured this row. Also this schema’s knowledge time and event time, because gauge reference data carries no upstream issuance stamp. Part of the dedup key, so it is never null

optional county: null | string

optional forecast_flood_category: null | string

the same categorization for the current forecast. Roughly one gauge in four carries a current forecast at any moment; the rest read fcst_not_current

gauge_id: string

the identifier the caller passed, echoed on every row of every flood product so a water frame and a gauge frame join on ONE column. On the NWPS leg it equals nws_location_id; both columns are still present so a caller never has to know which namespace a frame came from. No code path synthesizes a merged id

optional in_service: null | boolean

the upstream inService flag; NULL on an inventory row

optional latitude: null | number

units: degrees_north

optional longitude: null | number

units: degrees_east

optional major_flow_cfs: null | number

units: cfs

optional major_stage_ft: null | number

units: ft

optional minor_flow_cfs: null | number

units: cfs

optional minor_stage_ft: null | number

units: ft

optional moderate_flow_cfs: null | number

units: cfs

optional moderate_stage_ft: null | number

units: ft

optional monitoring_location_id: null | string

the same site number as usgs_site_id in USGS’s own prefixed spelling (USGS-02215260). Both are carried because the two upstreams address the same station differently

optional name: null | string

nws_location_id: string

the five-character NWS Location ID (ABBG1) the upstream calls ‘lid’

optional observed_flood_category: null | string

the NWS categorization of the current observed value: no_flooding | action | minor | moderate | major | low_threshold | not_defined | obs_not_current | out_of_service

raw_json: string

the verbatim upstream gauge object as JSON, for re-parse provenance. Every field this schema does not type — including the upstream unit strings and the free-text impact statements — survives here

optional reach_id: null | string

the National Water Model reach identifier, when the gauge has one

record_scope: "detail" | "inventory"

which route produced the row. ‘inventory’ rows come from the gauge LIST route, which serves no usgsId, no reachId, no flood thresholds and no timeZone — so on an inventory row those columns are NULL because the ROUTE omits them, not because the gauge lacks them. Roughly two gauges in three carry a USGS site number and three in four carry a reach id, so both readings of a NULL are common and the discriminator is what separates them

optional rfc: null | string

the River Forecast Center abbreviation that issues for this gauge

source: string

per-row source identity == df.attrs[‘source’]

optional state: null | string

thresholds_defined: boolean

True if and only if all four *_stage_ft values are non-NULL after missing-value normalization. Measured coverage across a 60-gauge sample: 10% had all four stage AND all four flow thresholds, and 47% had no stage thresholds at all — so a threshold comparison without this column reads ‘not flooding’ for a gauge that simply has nothing to compare against

optional timezone: null | string

the upstream timezone string, VERBATIM. It is a POSIX-style spelling and four of the eight distinct values observed across a gauge sample do not exist in tzdata — one of them pairs Eastern standard time with Central daylight time. It is never replaced by a guess; read timezone_loadable before using it

timezone_loadable: boolean

True when zoneinfo loads the timezone string above. Carried as DATA so an unusable zone costs a caller one column read instead of an exception on a gauge whose stage readings are fine

optional usgs_site_id: null | string

the bare USGS site number (02215260). NULL on an inventory row because that route omits it, and NULL on a detail row when the gauge genuinely has none — read record_scope to tell which

optional wfo: null | string


acquired_at_utc: string

EVENT TIME: the overpass instant. Built from the upstream date plus its zero-padded four-character time-of-day string, which is never read as an integer — that would turn an overpass just after midnight into one just after seven in the morning

attribution: string

the acknowledgment the provider requires, on EVERY row. It is a column rather than frame metadata because concat, merge, and groupby all drop df.attrs, and an acknowledgment that disappears on the first join is not an acknowledgment

optional brightness_k: null | number

units: K — the primary channel brightness temperature. Which physical channel that is depends on sensor_family, which is why the column is named for its role rather than for one family’s channel number

optional brightness_secondary_k: null | number

units: K — the second channel’s brightness temperature, on the same rule

captured_at: string

PROVENANCE STAMP and KNOWLEDGE TIME: when the SDK captured this row. Deliberately later than acquired_at_utc — the overpass instant is when the fire burned, and publication lags it by hours. The vintage dedup key, so it is never null

optional confidence_category: null | "high" | "low" | "nominal"

the imaging-radiometer family’s confidence, which the upstream reports as a word. NULL on the other family. A single ‘confidence at least x’ filter cannot span both dialects, which is exactly why they are two columns

optional confidence_percent: null | number

units: percent — the spectroradiometer family’s confidence, which the upstream reports as a percent from 0 to 100. NULL on the other family

optional detection_type: null | string

the upstream inferred-type code, which distinguishes a vegetation fire from a volcano, a static land source, and an offshore detection. Served on the archive product only, so it is NULL on a near-real-time row

optional fire_radiative_power_mw: null | number

units: MW — radiative power of the detected fire

optional is_daytime: null | boolean

whether the overpass was on the day side. The upstream spells this as a one-letter string; a two-valued flag stored that way is a filter every caller has to write twice

latitude: number

units: degrees_north

longitude: number

units: degrees_east

optional pixel_scan_km: null | number

units: km — the along-scan ground dimension of the detection pixel. The upstream header spells this one word with no unit, which is unreadable out of context; the token itself survives in raw_csv

optional pixel_track_km: null | number

units: km — the along-track ground dimension of the detection pixel. The upstream one-word spelling is not used here because the same word already names a hurricane best track in this schema family

product_version: string

the upstream processing version. Standard-quality data replaces the near-real-time product after roughly three months, so this is a vintage attribute: the SAME overpass yields a different row depending on when it was fetched

raw_csv: string

the verbatim upstream CSV record line, for re-parse provenance. Every upstream header token this schema renames survives here

optional region: null | string

the bulk file’s region token. NULL on the bounding-box route, which has no region — read the coordinates instead

satellite: string

the upstream platform token VERBATIM (N, N20, N21, A, T)

sensor_family: "modis" | "viirs"

which instrument family produced the row. It decides the brightness channels, the pixel footprint, and — see below — which of the two confidence columns is populated

source: string

per-row source identity == df.attrs[‘source’]

optional window: null | string

the bulk file’s look-back window token. NULL on the bounding-box route, which takes a day count instead


captured_at: string

PROVENANCE STAMP: when the SDK captured this row. Also this schema’s knowledge time and event time — a rating curve carries no upstream publication stamp

flow_cfs: number

units: cfs — the discharge this curve maps that stage to, in cubic feet per second

gauge_id: string

the identifier the caller passed; joins to schema.hazard.gauge.v1

optional nws_location_id: null | string

the NWS Location ID this curve belongs to. NULL when the caller addressed the gauge by its USGS site number — same reason as schema.hazard.stage_and_flow.v1’s column of this name: the ratings route takes either namespace and returns no lid

raw_json: string

the verbatim upstream curve point as JSON, for re-parse provenance

source: string

per-row source identity == df.attrs[‘source’]

stage_ft: number

units: ft — the curve’s independent variable; part of the row identity


captured_at: string

PROVENANCE STAMP: when the SDK captured this row. Part of the dedup key because only one forecast issuance is ever served — an uncaptured issuance cannot be fetched again

optional flow_cfs: null | number

units: cfs — flow_kcfs converted through the one shared unit table. The ONLY flow column comparable to schema.hazard.gauge.v1’s *_flow_cfs thresholds and to schema.hazard.rating_curve.v1

optional flow_kcfs: null | number

units: kcfs — the VERBATIM number this route serves, in kilo-cubic-feet per second. Comparing it to a gauge threshold in cubic feet per second is a thousandfold error; use flow_cfs for that

optional forecast_issued_at_utc: null | string

KNOWLEDGE TIME on a forecast row: when the River Forecast Center issued this forecast. NULL on an observed row. A forecast row is never joinable to an observation date without it

gauge_id: string

the identifier the caller passed; joins to schema.hazard.gauge.v1

optional generated_at_utc: null | string

the upstream production stamp, NOT a vintage key. It was measured four minutes later than the block issuance and is uniform across every point of one forecast block

optional latest_observation_at_utc: null | string

KNOWLEDGE TIME on an observed row: the timestamp of the newest observation in the block the row came from. The upstream serves this in the SAME field it uses for the forecast issuance, but it is not an issuance — so it gets its own column. NULL on a forecast row

optional nws_location_id: null | string

the NWS Location ID this series belongs to. NULL when the caller addressed the gauge by its USGS site number: this route accepts either namespace on the same path and its payload carries no lid, so there is no NWS Location ID to report. It is left NULL rather than filled with the site number, which would put a USGS identifier in a column whose name says NWS — read gauge_id for what the caller passed

raw_json: string

the verbatim upstream data point as JSON, for re-parse provenance. The block’s unit strings survive here

series: "forecast" | "observed"

which block of the upstream payload produced the row. Observed and forecast are never merged into one series: one is a measurement and the other is a prediction, and which of the two issuance columns below is populated depends on this value

optional shef_code: null | string

the SHEF physical-element code identifying the series (HGIRG, HGIFF)

source: string

per-row source identity == df.attrs[‘source’]

optional stage_ft: null | number

units: ft

optional timezone: null | string

the upstream timezone string verbatim; see schema.hazard.gauge.v1

valid_at_utc: string

EVENT TIME: the instant this stage or flow value describes

optional wfo: null | string


optional advisory_number: null | string

kept as a STRING: NHC zero-pads it (‘032’) and intermediate advisories carry a letter suffix (‘15A’), so an integer column would lose both

optional amendment_suffix: null | string

the ‘-AAA’ / ‘-BBB’ correction marker split off the product_id

optional awips_id: null | string

AWIPS identifier (PSHLIX). ID-FIRST, on the same rule as wmo_id. The live route serves no AFOS id, so there it always comes from the bulletin

captured_at: string

PROVENANCE STAMP: when the SDK captured this row (the vintage dedup key)

issued_at_utc: string

KNOWLEDGE TIME and EVENT TIME: for a text bulletin the two instants coincide — the bulletin becomes knowable at the moment it is issued

issuing_office: string

issuing office id (KNHC, KLIX); the per-entity join key

parse_status: "parsed" | "raw_only"

‘raw_only’ when the free-text parse missed; raw_afos is still complete

product_code: string

AFOS product code: TCP | TCM | TCD | TCV | PSH

product_id: string

primary key, e.g. ‘202607301834-KLIX-ACUS74-PSHLIX-AAA’. The trailing amendment suffix is PART of the key

raw_afos: string

verbatim AFOS bulletin text, for re-parse provenance

source: string

per-row source identity == df.attrs[‘source’]

optional storm_id: null | string

NHC storm id (AL142024) when the bulletin names one; null for PSH-by-office

optional storm_name: null | string

optional wmo_id: null | string

WMO product identifier (ACUS74). ID-FIRST: read from the AFOS product_id when it carries one, and from the bulletin’s WMO heading otherwise. The id is a strictly anchored parse of a value the archive guarantees; the heading is free text


optional advisory_number: null | string

zero-padded, may carry an intermediate suffix — a string, not an int

optional basin: null | string

AL | EP | CP, derived from the storm_id prefix

captured_at: string

PROVENANCE STAMP: when the SDK captured this row (the vintage dedup key)

optional classification: null | string

NHC classification: TD | TS | HU | PTC | STD | STS

optional forecast_advisory_url: null | string

optional forecast_discussion_url: null | string

optional intensity_kt: null | number

units: kt — maximum sustained wind; NHC serves it as a quoted string

optional last_update_at_utc: null | string

KNOWLEDGE TIME and EVENT TIME: NHC’s own lastUpdate stamp is both when the storm state was observed and when it became knowable

optional latitude: null | number

units: degrees_north

optional longitude: null | number

units: degrees_east

optional movement_direction_degrees: null | number

units: degrees

optional movement_speed_kt: null | number

units: kt

optional pressure_mb: null | number

units: hPa — minimum central pressure; NHC serves it as a quoted string

optional public_advisory_url: null | string

raw_json: string

verbatim NHC storm object as JSON, for re-parse provenance

source: string

per-row source identity == df.attrs[‘source’]

storm_id: string

NHC storm id (al142024 / ep072026); the per-entity join key

optional storm_name: null | string


optional approval_status: null | string

‘Provisional’ or ‘Approved’. The flip was measured between 60 and 90 days back, and it can change the VALUE, not only the label — which is why last_modified_at_utc is a dedup key rather than a display column

captured_at: string

PROVENANCE STAMP: when the SDK captured this row. Deliberately NOT part of this product’s dedup key — the upstream vintage already identifies the revision, so re-fetching is idempotent

gauge_id: string

the identifier the caller passed, echoed on every row so a water frame and a gauge frame join on one column instead of the caller choosing between three spellings of the same station

last_modified_at_utc: string

KNOWLEDGE TIME, and the UPSTREAM-PROVIDED vintage key: when USGS last revised this row. The legacy water-services host has no equivalent, which is why this schema does not accept it as a source. Because the vintage comes from upstream, re-fetching an unrevised row is idempotent

monitoring_location_id: string

the same site number in USGS’s own prefixed spelling (USGS-02215260)

parameter_code: string

the USGS parameter code (00065 = gage height, 00060 = discharge)

optional qualifier: null | string

the upstream per-row qualifier code, when present

raw_json: string

the verbatim upstream feature properties as JSON, for re-parse provenance

source: string

per-row source identity == df.attrs[‘source’]

optional statistic_id: null | string

the USGS statistic code, when the series declares one

time_series_id: string

the upstream’s stable per-series identifier. Part of the dedup key, because one station serves several series and a derived site:parameter:statistic string would not distinguish two sensors on the same parameter

optional timezone_abbreviation: null | string

the station’s standard-time abbreviation. With the flag below it gives local standard time directly, needing no tz-database lookup

optional units_raw: null | string

the upstream unit string verbatim. Unlike the NWPS routes — whose units are fixed per route and therefore belong in raw_json — this table is multi-parameter, so the unit varies row to row and has to be a column

optional uses_daylight_saving_time: null | boolean

whether the station observes daylight saving time

usgs_site_id: string

the bare USGS site number (02215260)

valid_at_utc: string

EVENT TIME: the instant this reading describes

optional value: null | number

the reading, coerced from the quoted string the upstream serves. Its unit is in units_raw, because it varies per parameter_code on this one schema


optional acres: null | number

units: acre — the currently reported size — the number that revises, and the reason this tier captures vintages at all

optional admin_unit: null | string

the administering unit; state leg only

optional agency_names: null | string

captured_at: string

PROVENANCE STAMP and KNOWLEDGE TIME: when the SDK captured this row. The vintage dedup key, so it is never null. The live interagency service drops a fire once it is contained and after a size-dependent staleness window, so an uncaptured revision cannot be fetched again

optional cause: null | string

optional cause_general: null | string

optional complex_id: null | string

optional complex_name: null | string

optional complexity_level: null | string

optional contained_at_utc: null | string

optional controlled_at_utc: null | string

optional coordination_center: null | string

the expanded name of what gacc_code designates — a Geographic Area Coordination Center. Carried beside the abbreviation so a caller reading a column list does not have to look one up

optional county: null | string

optional created_at_utc: null | string

when the upstream record was created (interagency leg)

optional discovered_at_utc: null | string

EVENT TIME: when the fire was discovered

optional discovery_acres: null | number

units: acre — the size at discovery; interagency leg only

optional dispatch_center: null | string

optional estimated_cost_to_date: null | number

units: USD

optional final_acres: null | number

units: acre — the certified size. Interagency leg only, and null long after a fire is out — the operational record is not revised once the fire report is closed

optional fips: null | string

the county FIPS code as a STRING; leading zeros are significant

optional gacc_code: null | string

the upstream coordination-centre token VERBATIM, because it is the join key and because a reader grepping the interagency documentation has to find it under the name it is published as

incident_id: string

primary key within one capture. The interagency leg carries the IRWIN incident GUID with its braces stripped; the state leg carries that feed’s own record UUID. The two namespaces never collide, so one physical fire seen by both feeds is two rows and the source column says which is which

optional incident_name: null | string

optional incident_type: null | string

the upstream incident category VERBATIM. The interagency feed mixes prescribed burns and incident complexes into every service, and the state feed mixes floods and hazardous-material responses into its list, so a caller summing acreage without reading this column counts intentional burns as wildfire

optional incident_url: null | string

optional is_active: null | boolean

the state feed’s own active flag; NULL on the interagency leg

optional is_complex_child: null | boolean

true when this incident rolls up into a complex. A complex record and its children are all in the feed, so summing acreage without reading this column double-counts every child

optional is_final: null | boolean

the state feed’s own final flag. It keeps moving after it turns true: the longest measured gap between a fire being declared extinguished and its record being modified again was 546 days

optional latitude: null | number

units: degrees_north — state leg only. The interagency route is queried with geometry off, which is this tier’s hard constraint, so an interagency row carries no coordinates

optional longitude: null | number

units: degrees_east

optional modified_at_utc: null | string

the upstream last-modified time; an ORDINARY column, never a vintage key. 9,098 of 14,587 out-fires in a year-to-date sample were modified more than a day after being declared out with the acreage frozen, so it records administrative churn rather than when a number became knowable

optional out_at_utc: null | string

when the fire was declared out. On the state leg the upstream spells an absent value as an empty string rather than null; the parser folds both to NULL

optional percent_contained: null | number

units: percent

optional raw_esri: null | string

verbatim upstream record in the format this column’s name declares (re-parse provenance): the interagency service’s attribute bag as JSON. Populated on the wfigs legs only

optional raw_json: null | string

verbatim upstream record in the format this column’s name declares (re-parse provenance): the state feed’s own record as JSON. Populated on the calfire leg only

source: string

per-row source identity == df.attrs[‘source’]

optional state: null | string

the bare two-letter state code. The interagency feed spells it with a country prefix and the state feed does not; both legs reduce to the bare token so they join on this column, and the served spelling survives in the row’s raw column

optional total_personnel: null | number

assigned personnel; the nullable integer dtype, so absent stays absent

optional unique_fire_identifier: null | string

the interagency year + protecting-unit + local-identifier string; the stable cross-year join key when one exists


Options for hotspots().

  • FetchWithRetryOptions

optional bbox: HotspotBbox

The area to read, ordered west, south, east, north.

REQUIRES source: "firms.area" and a key of your own: the bulk tier serves fixed regional files, so honouring a box with one would return the wrong geography under a right-looking call. Passing it without that source throws and names both the source and the environment variable.

optional fromTime: Date

Earliest overpass instant to keep. Given together with toTime or not at all — one bound alone throws, because inventing the other decides which overpasses come back. Both unset means the most recent published look-back. The verb derives the upstream’s own window token from the pair and filters the parsed rows to the requested span.

optional region: FirmsRegion

Which published region. Defaults to "usa". Refused with source: "firms.area", which is addressed by bbox instead — silently ignoring one of the two would return the wrong geography under a right-looking call.

optional sensor: FirmsSensor

Which platform. Defaults to "viirs_snpp".

optional source: HotspotSource

Which route answers. Unset means the keyless bulk tier.

optional toTime: Date

Latest overpass instant to keep. Given together with fromTime.


Optional knobs for iemMosForecasts.

readonly optional fetchFn: (input, init?) => Promise<Response>(input, init?) => Promise<Response>

Override the fetch function (used by tests).

MDN Reference

URL | RequestInfo

RequestInit

Promise<Response>

MDN Reference

string | URL | Request

RequestInit

Promise<Response>

readonly optional model: IemMosModel

Default "nbe".

readonly optional signal: AbortSignal

Caller abort signal. Propagated to every per-cycle fetch; an abort during an in-flight request rejects promptly with the platform AbortError.


One IEM MOS forecast row.

readonly dewPointC: null | number

2-m dew point in Celsius.

readonly forecastHour: number

(validAt - issuedAt) in hours.

readonly issuedAt: string

Model run datetime — UTC ISO string.

readonly model: string

UPPERCASE model id (e.g. "NBE").

readonly precipProbability: null | number

12-hour probability of precipitation [0, 1].

readonly retrievedAt: string

When the row was fetched — UTC ISO string.

readonly skyCoverPct: null | number

Sky cover %; IEM MOS does not expose this — always null.

readonly source: IemMosSource

Per-row source identity. Always "iem.archive" for now.

readonly station: string

ICAO station code (uppercased).

readonly tempC: null | number

2-m temperature in Celsius (null when MOS field is M / missing).

readonly validAt: string

Forecast valid datetime — UTC ISO string.

readonly windDirDeg: null | number

Wind direction in degrees [0, 360).

readonly windSpeedMs: null | number

10-m wind speed in m/s.


One issuing office as api.weather.gov serves it.

A record with snake_case keys, not a Record<string, string> map: the product code is part of the answer (the same office issues different products under different codes), and a bare map would have nowhere to put it.

code: string

Office identifier as served, e.g. "HGX".

name: string

Human-readable office name, e.g. "Houston TX".

product_code: string

The AWIPS product code this list was requested for, e.g. "PSH".


  • FetchWithRetryOptions

optional productCode: string

AWIPS product code to list offices for. Default "PSH".


readonly optional source: null | string

Live source to poll. "awc" (default, fastest) or "iem" (~10-min delay; useful when AWC is down). Case-insensitive.


Single observation row, matching specs/observation.json.

Notes on null vs unknown:

  • Every field defaults to null if the upstream record omits it, fails bounds, or fails type parsing. This mirrors the Python parser (no exceptions on bad input — only on missing required keys).
  • source is always the string literal "awc" for AWC-sourced rows (the row-level enum is "awc" | "iem" | "ghcnh"; the .live/.archive suffix is a catalog source-id, not a row field).
  • event_time_utc is ISO 8601 UTC with Z suffix.

readonly altimeter_inhg: null | number

readonly dewpoint_c: null | number

readonly dewpoint_f: null | number

readonly event_time_utc: string

readonly observation_type: "METAR" | "SPECI"

readonly peak_wind_dir: null | number

readonly peak_wind_gust_kt: null | number

readonly peak_wind_time: null | string

readonly precipitation_in_1h: null | number

readonly qc_field: null | number

readonly raw_metar: null | string

readonly sea_level_pressure_hpa: null | number

readonly sky_base_1_ft: null | number

readonly sky_base_2_ft: null | number

readonly sky_base_3_ft: null | number

readonly sky_base_4_ft: null | number

readonly sky_cover_1: null | string

readonly sky_cover_2: null | string

readonly sky_cover_3: null | string

readonly sky_cover_4: null | string

readonly snow_depth_inches: null | number

readonly source: "awc" | "ghcnh" | "iem"

Per-row source tag for AWC live, IEM ASOS archive, or GHCNh archive. Each parser still emits its own literal — AWC emits "awc", IEM ASOS emits "iem", GHCNh emits "ghcnh" — but the shared Observation contract accepts all three so mergeObservations sees one row shape across sources.

readonly station_code: string

readonly temp_c: null | number

readonly temp_f: null | number

readonly visibility_miles: null | number

readonly weather_codes: null | string

readonly wind_dir_degrees: null | number

readonly wind_gust_kt: null | number

readonly wind_speed_kt: null | number


optional reportType: "metar" | "speci" | "all"

Which report kinds to return — "all" (default, both hourly METAR and off-hour SPECI), "metar" (hourly only), or "speci" (special reports only). Filters on the per-row observation_type. Mirrors Python observations(..., report_type=...). Note the TS IEM leg fetches reportType: 3 (METAR) from IEM, so a "speci" filter returns only AWC SPECI rows; IEM SPECI rows are not fetched in TypeScript.

optional signal: AbortSignal

Caller abort signal, propagated to every IEM/AWC fetch (and composed with each attempt’s retry backoff wait). An abort mid-flight rejects promptly with the platform AbortError — never reclassified as a transport error.

optional source: ObsSourceFilter

Optional source filter. null (default) means all sources merged.

optional strategy: ObsStrategy

Strategy mode; default "auto".

optional timezone: string

IANA timezone override forwarded to the local_standard_date LST-bucketing machinery (mirrors Python observations(..., timezone=...)). Rarely needed for the canonical station registry (all covered); used for stations whose tz is not in the registry.


Single observation row. Field set mirrors the canonical Observation schema (@mostlyrightmd/weather Observation interface) with the METAR-derived fields (temp_f / dewpoint_f / wind_speed_kts / …).

optional dewpoint_c: null | number

optional dewpoint_f: null | number

event_time_utc: string

optional knowledge_time: string

Point-in-time key = event_time_utc + the per-source broadcast lag (awc +5min, iem +15min, ghcnh +6h), serialized as an ISO-8601 UTC string. Mirrors Python’s knowledge_time column (a tz-aware datetime64 there; a plain ISO string here — JS has no datetime64). Present on every per-report row with a parseable event_time_utc.

optional local_standard_date: string

ISO YYYY-MM-DD string of the local standard day this report belongs to, computed via localStandardDateFor (never a raw event_time_utc[:10] UTC-day slice — a pre-midnight-UTC METAR belongs to the PRIOR LST day for negative-offset US stations). Present on every per-report row with a parseable event_time_utc; it is the per-report join key the composed observation-grain dataset uses to LEFT-join daily columns onto each report.

optional observation_type: "METAR" | "SPECI"

"METAR" (hourly) | "SPECI" (off-hour special). Mirrors Python’s observation_type column; the reportType option filters on it.

optional precipitation_mm_1h: null | number

optional pressure_inhg: null | number

optional raw_metar: null | string

source: string

station: string

temp_c: null | number

temp_f: null | number

optional wind_direction_degrees: null | number

optional wind_speed_kts: null | number


Optional knobs for openMeteoForecasts.

readonly optional allowLeakage: boolean

Required true to invoke mode: "seamless".

readonly optional fetchFn: (input, init?) => Promise<Response>(input, init?) => Promise<Response>

Override the fetch function (used by tests).

MDN Reference

URL | RequestInfo

RequestInit

Promise<Response>

MDN Reference

string | URL | Request

RequestInit

Promise<Response>

readonly optional issuedAt: string

Optional ISO cycle for Single Runs API dispatch.

readonly optional mode: OpenMeteoMode

Default "training".

readonly optional model: OpenMeteoModel

Default "gfs_global".

readonly optional signal: AbortSignal

Caller abort signal. Propagated to the forecast fetch; an abort during an in-flight request rejects promptly with the platform AbortError.


One Open-Meteo forecast row (schema.forecast.station.v1).

readonly apparentTempC: null | number

readonly capeJkg: null | number

readonly cloudCoverPct: null | number

readonly dewPointC: null | number

readonly directRadiationWm2: null | number

readonly forecastHour: null | number

(validAt - issuedAt) in hours. null when issuedAt is null.

readonly freezingLevelM: null | number

readonly issuedAt: null | string

Model run datetime — UTC ISO string. May be null for source="open_meteo.seamless" rows (cycle unrecoverable by design).

readonly model: string

Open-Meteo model key (lowercase, e.g. "gfs_global").

readonly precipitationMm: null | number

readonly precipProbability: null | number

readonly pressureMslHpa: null | number

readonly retrievedAt: string

readonly shortwaveRadiationWm2: null | number

readonly snowDepthM: null | number

readonly source: OpenMeteoSource

readonly station: string

readonly surfacePressureHpa: null | number

readonly tempC: null | number

readonly validAt: string

readonly visibilityM: null | number

readonly weatherCode: null | number

readonly windDirDeg: null | number

readonly windGustsMs: null | number

readonly windSpeedMs: null | number


Options for postStormReports.

Same shape as TextProductsOptions minus productCode: a post-storm report is PSH by definition, and offering the code would let a caller ask this verb for something that is not a post-storm report.

optional fromTime: string

Window start, ISO-8601 with a Z offset.

Omit.fromTime

optional limit: number

Page size for the live route.

Omit.limit

optional office: string

Issuing office (AFOS cccc), e.g. "KNHC". Archive route only.

Omit.office

optional productId: string

Fetch one product by id. An identity call: a product id that yields no bulletin text throws NoHazardDataError naming the id, never an empty result. Ignores fromTime / toTime.

Omit.productId

optional source: TextProductsSource

Which route answers. Unset auto-routes on the window age.

Omit.source

optional toTime: string

Window end, ISO-8601 with a Z offset.

Omit.toTime

optional vintages: Vintages

Row selector. Default "all". See Vintages.

Omit.vintages


Options for stageAndFlow().

  • FetchWithRetryOptions

optional series: WaterSeries

Read one series instead of both. Unset reads the combined document and returns BOTH products in one result, discriminated by the series column.

The option is called series and not product because product_code already names an NWS text bulletin in this schema family, and one word must not name two things. A value the route does not serve throws before any request.


readonly optional pollSeconds: null | number

Override the polite-floor cadence. Must be >= the per-source floor (AWC=30, IEM=60). When omitted, uses the floor for the active source.

readonly optional signal: AbortSignal

Optional AbortSignal for clean cancellation. When fired, the current polite-floor sleep is interrupted and the generator returns. The current in-flight fetch (if any) is allowed to complete — AbortSignal is not threaded into the underlying fetchers, which wrap the platform fetch synchronously.

readonly optional source: null | string

Live source to poll. "awc" (default) or "iem". Case-insensitive.


  • FetchWithRetryOptions

optional fromTime: string

Window start, ISO-8601 with a Z offset.

optional limit: number

Page size for the live route.

optional office: string

Issuing office (AFOS cccc), e.g. "KNHC". Archive route only.

optional productCode: string

AWIPS product code. Default "TCP" (the public advisory).

optional productId: string

Fetch one product by id. An identity call: a product id that yields no bulletin text throws NoHazardDataError naming the id, never an empty result. Ignores fromTime / toTime.

optional source: TextProductsSource

Which route answers. Unset auto-routes on the window age.

optional toTime: string

Window end, ISO-8601 with a Z offset.

optional vintages: Vintages

Row selector. Default "all". See Vintages.


  • FetchWithRetryOptions

optional source: "nhc"

Which route answers. Default "nhc".

optional vintages: Vintages

Row selector. Default "all". See Vintages.


Options for waterObservations().

  • FetchWithRetryOptions

optional approvalStatus: WaterApprovalStatus

Keep only rows in this state.

An ordinary FILTER over a column, never a vintage selector: “provisional” is a property of the datum, not of when this install learned it. The knowledge time is last_modified_at_utc, which the upstream supplies and every row carries. A value USGS does not publish throws before any request.

optional parameterCode: "00065" | "00060"

USGS parameter code: "00065" (gage height, ft — the default) or "00060" (discharge, ft³/s).

A closed set, not a free string: anything else returns rows whose units no column on this schema names. Checked at compile time by this union and at runtime in fetchContinuous, matching _check_parameter_code.


Options for wildfires().

  • FetchWithRetryOptions

optional activeOnly: boolean

Ask the California route for the incidents burning right now rather than the whole season. Refused with the interagency source.

optional fromDate: string

Earliest discovery day to keep. Refused with source: "calfire", and refused against scope: "current" when it predates the fall-off horizon.

optional incidentType: null | string

The incident category to keep.

Interpreted PER SOURCE, because the two upstreams genuinely use different vocabularies and this SDK does not invent a third to paper over it: on the interagency leg it is a category ("WF", "RX", "CX"), and on the state leg it is a type ("Wildfire", "Fire", "Flood", "Hazmat"). The default is "WF", and the state leg reads that default as its own "Wildfire". null disables the filter and returns every category the leg serves.

optional includeComplexChildren: boolean

Keep the child incidents that roll up into a complex. Defaults to false, because a complex parent and its children both report acreage and summing both double-counts. Refused with source: "calfire".

optional minAcres: number

Keep only incidents at or above this size in acres. Refused with source: "calfire".

optional scope: WfigsScope

Which interagency window. Defaults to "current". Refused with source: "calfire".

optional source: WildfireSource

Which provider answers. Unset means the interagency feed.

optional state: string

A two-letter state code, matched against the upstream’s country-prefixed token. Refused with source: "calfire", whose every row is California.

optional toDate: string

Latest discovery day to keep, inclusive. Refused with source: "calfire".

optional year: number

Which California season. Refused with the interagency source, which selects a window through scope instead.

AlertsSource: "nws" | "iem"

Which route answers.

"nws" (the default) reads the live CAP feed at api.weather.gov/alerts, which retains exactly 7 days; a fromTime older than that throws HazardRetentionError instead of returning an empty result.

"iem" reads the Iowa State VTEC archive, which covers 2005 onward and has no retention cliff. One verb, one source axis — there is no separate alertHistory verb to remember.


CalendarDay: string

A calendar DAY, spelled YYYY-MM-DD.

Deliberately not a Date: a Date is an instant, and reading a calendar day off one needs a timezone this SDK would have to guess. Python’s twin takes a datetime.date, which TypeScript has no equivalent of, so the string spelling is the honest one here. Both SDKs convert the day to an absolute epoch-millisecond boundary before it reaches a query.


DailyExtremesMergeMode: "live_v1" | "awc_only" | "iem_only"

Merge mode controlling which sources contribute observations.

  • live_v1 (default) — IEM ASOS for historical depth + AWC for the recent 168h window. Matches Python merge="live_v1".
  • awc_only — AWC live METAR only. Window must be inside the 168h AWC retention or callers see a sparse return.
  • iem_only — IEM ASOS archive only. No live fallback.

FirmsRegion: "usa" | "alaska" | "canada" | "global"

The four regions the keyless bulk tier publishes.


FirmsSensor: "viirs_snpp" | "viirs_noaa20" | "viirs_noaa21" | "modis"

The four platforms both routes serve.


GaugeBbox: readonly [number, number, number, number]

A bounding box as the NWPS inventory route states it: [xmin, ymin, xmax, ymax] — west, south, east, north — in the units of GaugesOptions.srid.


HotspotBbox: readonly [number, number, number, number]

A bounding box in degrees, ordered west, south, east, north.


HotspotSource: "firms.bulk" | "firms.area"

Which detection route answers.

Unset and "firms.bulk" both take the KEYLESS bulk tier, which is the default precisely so an install that has never registered for anything gets working detections — and so a build that reaches only this route never reads a key. "firms.area" takes the bounding-box route, which needs a key of your own in MOSTLYRIGHT_FIRMS_MAP_KEY.


IemMosModel: "nbe" | "gfs" | "lav" | "met" | "ecm"

IEM MOS model enum.


IemMosSource: "iem.archive" | "iem.live"

Canonical source enum.


LiveObservation: Omit<Observation, "source"> & object

Observation row emitted by mostlyright.live.stream and live.latest.

Same shape as the canonical Observation row, but with source narrowed to the live-channel identity tags. The archive-channel "awc" / "iem" / "ghcnh" source values are not valid here, which is why live rows carry their own type.

readonly source: LiveSourceTag


LiveSource: typeof SUPPORTED_SOURCES[number]

Validated source enum derived from SUPPORTED_SOURCES.


LiveSourceTag: typeof SOURCE_IDENTITY_TAGS[LiveSource]


NwpModel: "hrrr" | "gfs" | "nbm" | "hrrrak" | "gefs" | "gdas" | "rap" | "rrfs" | "rtma" | "urma" | "cfs" | "ecmwf_ifs_hres" | "ecmwf_ifs_ens" | "ecmwf_aifs_single" | "ecmwf_aifs_ens" | "hrdps" | "rdps" | "gdps" | "geps" | "reps" | "hafs" | "nam" | "href" | "hiresw"

Supported NWP model identifiers.


ObsFrameSource: "merged.live_v1" | "awc" | "iem" | "ghcnh"

Frame-level source identity — serialized as provenance.source on the @mostlyrightmd/core!DataResult returned by observations(). Value mirrors Python df.attrs["source"]:

  • unpinned (source omitted / null) → "merged.live_v1" (the merged-frame tag, which a single-source/pinned schema rejects).
  • pinned (source set) → the bare pinned source ("awc" / "iem").

ObsSourceFilter: "awc" | "iem" | "ghcnh" | null

Source filter — matches Python weather.observations(source=...) and its _VALID_SOURCES frozenset {"awc", "iem", "ghcnh"}. null (default) means all sources merged.

ghcnh validates as accepted but is not available through this TypeScript path; selecting it raises DataAvailabilityError(reason="model_unavailable") rather than returning [].


ObsStrategy: "auto" | "exact_window" | "warm_cache" | "hosted"

Observation fetch strategy.


OpenMeteoMode: "training" | "live" | "seamless"

Open-Meteo dispatch mode for openMeteoForecasts.


OpenMeteoModel: "gfs_seamless" | "gfs_global" | "gfs_graphcast025" | "aigfs025" | "hgefs025" | "ncep_hrrr_conus" | "ncep_nbm_conus" | "ncep_nam_conus" | "ecmwf_ifs025" | "ecmwf_ifs_hres" | "ecmwf_aifs025_single" | "dwd_icon_seamless" | "dwd_icon_global" | "dwd_icon_eu" | "dwd_icon_d2" | "dwd_icon_d2_15min" | "meteofrance_seamless" | "meteofrance_arpege_world025" | "meteofrance_arpege_europe" | "meteofrance_arome_france0025" | "meteofrance_arome_france_hd" | "meteofrance_arome_france_hd_15min" | "jma_seamless" | "jma_gsm" | "jma_msm" | "kma_seamless" | "kma_gdps" | "kma_ldps" | "cma_grapes_global" | "bom_access_global" | "ukmo_global_deterministic_10km" | "ukmo_uk_deterministic_2km" | "metno_nordic_pp" | "cmc_gem_gdps" | "cmc_gem_rdps" | "cmc_gem_hrdps"

The 36 Open-Meteo forecast models in scope.


OpenMeteoSource: "open_meteo.previous_runs" | "open_meteo.single_run" | "open_meteo.live" | "open_meteo.seamless"

Open-Meteo source-identity enum (per-endpoint discrimination).


RatingCurveOptions: FetchWithRetryOptions

Options for ratingCurve().

Transport controls only, on purpose. The measured curve is 303 points and 16,169 bytes and always arrives whole and ascending by stage_ft, so slicing options over a small ordered table buy a caller nothing that an array slice does not already give them. The Python twin refuses the same ones.


ReportType: "final" | "ncei_final" | "correction" | "preliminary" | "estimated"


TextProductsSource: "nws" | "iem"

Which route answers.

"nws" reads api.weather.gov/products, which retains 7 days. "iem" reads the Iowa State AFOS archive, the only route that carries intermediate advisories (the ones with a letter suffix) and PSH amendments.

Left unset, the verb chooses: a window inside the 7-day retention goes to the live route, an older one auto-routes to the archive. Unlike alerts, an old window here is a route change, not a rejection — the archive serves the same product, so there is nothing for the caller to decide.


TropicalCyclonesSource: "nhc"

Which route answers. One value today: NHC’s CurrentStorms.json is the only feed that serves active tropical cyclones, and there is no archive of that document. The option exists so a second leg can be added without changing the call shape.


Vintages: "all" | "latest"

Row selector for the hazard verbs.

"all" (the default) keeps every row the route returned, including repeated captures of the same identity. "latest" keeps one row per identity — the one with the greatest captured_at, ties broken by upstream order.

Unlike Python, the TypeScript SDK keeps no local capture ledger: nothing is persisted, so the selector applies to rows the caller has already fetched in this call rather than to a stored vintage history.

@mostlyrightmd/economy’s series.ts exports a different type under the same name, accepting "settlement" | "all". The two are unrelated; check which package a Vintages value is destined for before passing a literal.

What “one row per identity” means differs per verb, and each one matches the Python partition key in hazards/_cache.py’s _HAZARD_PARTITION_KEYS so the same keyword performs the same reduction in both SDKs:

verbidentity
alerts(alert_id, ugc_zone)
textProducts / postStormReports(product_id), amendment suffix included
tropicalCyclones(storm_id, last_update_at_utc) — one row per storm state, not per storm
bestTrack(storm_id, valid_at_utc)

WaterApprovalStatus: "Provisional" | "Approved"

The two states USGS publishes a continuous value in.


WaterSeries: "observed" | "forecast"

One of the two series the stage-and-flow route serves.


WfigsScope: "current" | "season" | "history"

Which interagency window answers.

"current" serves the fires that are neither contained, controlled, nor out AND have been updated recently — it drops records under published fall-off rules, so it cannot answer a past window. "season" serves the whole current calendar year with no fall-off. "history" serves every year the interagency reporting system has recorded.


WildfireSource: "wfigs" | "calfire"

Which provider answers.

The provenance axis, mirroring the Python source= set. Unset and "wfigs" both serve the national interagency feed through scope; "calfire" serves the California state feed through year / activeOnly. There is no separate California verb, and the name it would have had is spelled nowhere in this package so a grep for it stays a gate: a second name for the same row contract differing only in who produced it is exactly what this axis exists to express.

const FIRMS_ATTRIBUTION: string

The acknowledgment NASA asks for, verbatim.

Byte-identical to Python’s FIRMS_ATTRIBUTION. Written into the attribution COLUMN of every hotspot row, and carried on the result metadata too — including on an empty result, where there is no row to carry it.


const HIGH_TEMP_MAX_F: 150 = 150


const HIGH_TEMP_MIN_F: -60 = -60

Climate temp bounds from specs/climate.json. Inclusive.


const LOW_TEMP_MAX_F: 130 = 130


const LOW_TEMP_MIN_F: -80 = -80


const OPEN_METEO_MODELS: ReadonlySet<OpenMeteoModel>

The canonical 36-model set. The test suite compares this by set equality, so an added or dropped model fails rather than passing silently.


const POLITE_FLOORS_S: Readonly<Record<LiveSource, number>>

Minimum allowed poll cadence per source, in seconds.

  • AWC: 30s — aviationweather.gov has no documented rate limit but 30s is the empirically-validated floor that won’t trip anti-abuse heuristics.
  • IEM: 60s — mesonet.agron.iastate.edu is a university server; IEM docs explicitly ask for reasonable headroom above 1 req/s.

const SOURCE_IDENTITY_TAGS: object

Canonical per-source source field tag emitted on every observation row.

"awc.live" / "iem.live" are the live-channel identity tags — distinct from the archive-channel "awc" / "iem" written by the historical fetchers. Cross-SDK parity: these match Python SOURCE_IDENTITY_TAGS.

readonly awc: "awc.live" = "awc.live"

readonly iem: "iem.live" = "iem.live"


const SUPPORTED_SOURCES: readonly ["awc", "iem"]

Canonical ordered tuple of supported sources. Order matters — keep AWC first.


const version: "4.1.0" = "4.1.0"

Public package version; matches package.json.

alerts(opts): Promise<DataResult<HazardAlertV1>>

Fetch CAP hazard alerts at the (issuance x UGC zone) grain.

Mirrors Python mostlyright.weather.hazards.alerts(...). Returns DataResult<HazardAlertV1>: snake_case rows plus the provenance envelope whose source is the tag of the leg that answered ("nws.alerts.live" or "iem.vtec.archive"). Every row carries the same tag in its own source column.

One alert covering two zones produces two rows sharing alert_id. An alert that was updated appears once per issuance, with references recording which issuance each one supersedes — do not collapse on (zone, event), that discards the supersession history this adapter exists to capture.

AlertsOptions = {}

Promise<DataResult<HazardAlertV1>>

HazardRetentionError when fromTime predates the live route’s 7-day retention and source is not "iem". Thrown before any request.

ContractError when source: "iem" is asked for without a window (the archive route has no “everything” mode), or is combined with event (the archive serves VTEC codes, not NWS event names).


bestTrack(stormId, opts): Promise<DataResult<HazardBestTrackV1>>

Fetch the HURDAT2 best-track fixes for one storm.

string

NHC storm id, e.g. "AL142024". Matched case-insensitively.

BestTrackOptions = {}

Promise<DataResult<HazardBestTrackV1>>

Node-only. www.nhc.noaa.gov sends no Access-Control-Allow-Origin header (measured 1 August 2026); calling this from a browser page will fail CORS, so this verb refuses there with a typed ContractError. A Node process, a Cloudflare Worker, or an extension service worker with www.nhc.noaa.gov in host_permissions can call it.

Mirrors Python mostlyright.weather.hazards.best_track(...). Returns DataResult<HazardBestTrackV1> stamped "nhc.hurdat2", with provenance.data_version carrying the exact file name the rows came from — the vintage identity, since HURDAT2 has no stable “latest” alias.

Each row keeps both instants: hurdat2_revision is when the values became knowable, valid_at_utc is the synoptic fix. For a re-analysed storm they are years apart, which is the whole point of keeping both. landfall_flag carries HURDAT2’s L record identifier — the settlement-relevant flag for “hurricane hits X” contracts.

NoHazardDataError when the selected revision carries no such storm.


bestTrackRevisions(opts): Promise<readonly BestTrackRevision[]>

List every HURDAT2 reanalysis file the NHC directory serves.

BestTrackRevisionsOptions = {}

Promise<readonly BestTrackRevision[]>

Node-only. www.nhc.noaa.gov sends no Access-Control-Allow-Origin header (measured 1 August 2026); calling this from a browser page will fail CORS, so this verb refuses there with a typed ContractError. A Node process, a Cloudflare Worker, or an extension service worker with www.nhc.noaa.gov in host_permissions can call it.

This verb enumerates files rather than rows, so it resolves to readonly BestTrackRevision[] — the one declared non-DataResult return in the hazard family. There is no row grain here and no schema id to stamp.

Ordered ascending by revision, never by file name: the two orders disagree, because the date encoding in the name is six digits in some years and eight in others.


dailyExtremes(station, fromDate, toDate, opts): Promise<DataResult<DailyExtremeRow>>

Compute per-day tmin/tmax/tmean/precip for a station’s window.

Matches Python mostlyright.international.daily_extremes signature. Day-bucketing uses the station’s IANA local timezone from the STATIONS registry; US ASOS stations get integer-°F precision, other stations get 0.1-precision values.

string

4-letter ICAO (e.g. “KNYC”, “EGLL”) or 3-letter NWS registry code (e.g. “NYC”); Python daily_extremes() accepts both. Output rows carry the ICAO whichever form was passed in.

string

ISO date YYYY-MM-DD (inclusive, station-local)

string

ISO date YYYY-MM-DD (inclusive, station-local)

DailyExtremesOptions = {}

optional merge mode (default "live_v1")

Promise<DataResult<DailyExtremeRow>>

array of DailyExtremeRow, one per station-local day

Error if station is not in the STATIONS registry


forecastNwp(station, model, _opts): Promise<never>

Fetch a gridded NWP forecast.

string

NwpModel

ForecastNwpOptions = {}

Promise<never>

Not available in TypeScript. This function throws on every call. Use the Python SDK for gridded NWP.

GRIB2 decoding requires native libraries (eccodes C library or cfgrib Python wrapper). No production-ready browser-side decoder is available, and a WASM port’s compile time and bundle size are impractical.

Workaround paths:

  • 7 major US stations (KNYC, KLAX, KORD, KMIA, KDEN, KSEA, KATL) → iemMosForecasts ships MOS-based forecasts that solve most use cases. The error hint includes this pointer automatically.
  • Everything else → use the Python SDK (pip install mostlyrightmd-weather).

NwpNotAvailableError on every call. The error is a subclass of DataAvailabilityError, so existing catch (e instanceof DataAvailabilityError) paths continue to catch it. The thrown instance carries typed .station and .model properties for log/error attribution.

docs/nwp-forecasts.md for the supported-model list and current alternatives.


gauges(opts): Promise<DataResult<HazardGaugeV1>>

Return NWPS river gauges — a bounding box of them, or one by identifier.

GaugesOptions = {}

Promise<DataResult<HazardGaugeV1>>

One verb, two scopes, one identifier word. bbox enumerates the gauge INVENTORY inside a box, tiling the request because the route serves no paging and its unfiltered answer was measured at 13 MB and a gateway timeout. gaugeId returns the ONE-row DETAIL record, which is the only place the identifier crosswalk, the timezone, and the flood-category thresholds are served. record_scope says which shape a row is.

Runs in a browser and in Node. Mirrors Python mostlyright.weather.hazards.gauges(...) and returns DataResult<HazardGaugeV1> stamped "nwps.gauge.live".

On an inventory row usgs_site_id, reach_id, timezone, and the eight threshold columns are null because the LIST route omits them, not because the gauge lacks them — read record_scope to tell the two apart. Flood thresholds are mostly absent upstream: in a 60-gauge sample 10% carried all four stage AND all four flow thresholds and 47% carried no stage threshold at all, which is what thresholds_defined makes visible as a column.

ContractError when both selectors are given or neither; when the box is inverted, zero-width, or off the globe; or when the identifier is not one the routes address.

NotFoundError when gaugeId addresses no gauge. An identity failure is never converted into an empty result.

const basin = await gauges({ bbox: [-84, 33, -83, 34] });
basin.rows[0].observed_flood_category;
const detail = await gauges({ gaugeId: "ABBG1" });
detail.rows[0].action_stage_ft;

hotspots(opts): Promise<DataResult<HazardHotspotV1>>

Return satellite thermal-anomaly detections.

HotspotsOptions = {}

Promise<DataResult<HazardHotspotV1>>

NODE-ONLY, both routes: firms.modaps.eosdis.nasa.gov sends no Access-Control-Allow-Origin header on either data route (measured 1 August 2026), so a browser page is refused at call time with a typed ContractError rather than an opaque CORS failure. And because the key is a URL PATH SEGMENT, a browser call on the keyed route would expose it to anyone who can read the request — which is why the KEYLESS bulk tier is the default and why hotspots() with no bounding box reads no MOSTLYRIGHT_FIRMS_MAP_KEY. See docs/browser-integration.md.

With no arguments this reads the most recent published look-back file for the default platform over the contiguous United States and Hawaii, keyless, and returns DataResult<HazardHotspotV1> tagged "firms.bulk.live".

ATTRIBUTION. NASA asks for an acknowledgment, and it is written into the attribution COLUMN of every row rather than into result metadata alone — a column survives the reshaping a caller does, and an acknowledgment that disappears on the first join is not an acknowledgment. On an empty result there is no row to carry it, so the exported FIRMS_ATTRIBUTION constant is the reachable copy. Python additionally stamps it on the frame’s attribute bag, which the DataResult envelope has no equivalent of: that envelope’s key set is pinned across every verb in this SDK.

KEYLESS names the FIRMS credential. hotspots() needs no NASA registration and reads no MOSTLYRIGHT_FIRMS_MAP_KEY, but its address resolves through the manifest catalog like every other source here, so MOSTLYRIGHT_API_KEY is read and a caller with no key at all gets ApiKeyRequiredError on BOTH routes.

persist is Python-only; see ./hotspots.types.ts.

ContractError when source, sensor, or region is outside its table; when bbox is passed without source: "firms.area" (the message names both that source and MOSTLYRIGHT_FIRMS_MAP_KEY); when region is passed with it; when the window is inverted or wider than the route can answer; or when the keyed route is called with no key configured. It never falls back to the keyless tier.

SourceUnavailableError when a route answers a non-2xx. The message carries a redacted address.

ApiKeyRequiredError when no API key is configured for catalog resolution. This is the SDK’s own MOSTLYRIGHT_API_KEY, not the FIRMS one, and it applies to both routes.

const recent = await hotspots();
recent.rows.map((row) => [row.latitude, row.longitude, row.acquired_at_utc]);
const box = await hotspots({
source: "firms.area",
bbox: [-124.5, 32, -114, 42],
});

iemMosForecasts(station, fromDate, toDate, opts): Promise<DataResult<IemMosRow>>

Fetch IEM MOS forecasts for station in [fromDate, toDate].

Mirrors Python fetch_iem_mos(...). Iterates the model’s runtime-hour grid (NBE moved from {01,07,13,19}Z to {00,06,12,18}Z on 2026-05-05; other models use {00,06,12,18}Z), GETs the JSON endpoint, and projects rows to IemMosRow.

404 responses are silently skipped (many runtimes have no MOS data). Empty input range returns [].

string

string

string

IemMosOptions = {}

Promise<DataResult<IemMosRow>>

Error if model is not in SUPPORTED_MODELS.


isLiveSource(s): s is “awc” | “iem”

Type guard: narrow a string to LiveSource.

string

s is “awc” | “iem”


issuingOffices(opts): Promise<readonly IssuingOffice[]>

Fetch the issuing offices that publish one product code.

Mirrors Python mostlyright.weather.hazards.issuing_offices(...). Reads the served api.weather.gov/products/types/{code}/locations route, so the list stays current — no office code is hardcoded anywhere in this module.

Returns readonly IssuingOffice[] sorted by code: a record list rather than a Record<string, string> map, because the product code is part of the answer and a bare map has nowhere to put it.

IssuingOfficesOptions = {}

Promise<readonly IssuingOffice[]>


latest(station, opts): Promise<LiveObservation>

Return the most-recent observation row for station from a single source.

Same fetch path as stream, but returns once instead of looping. Use this for cron-style polling where you want one fresh observation per invocation.

string

ICAO ("KNYC") or 3-letter US ID ("NYC"). Case-insensitive.

LatestOptions = {}

Optional { source }.

Promise<LiveObservation>

Error when opts.source is unknown.

NoLiveDataError when the upstream returned no observations for the station — payload carries the resolved station and live source tag for branching.


observations(rawStation, fromDate, toDate, opts): Promise<DataResult<ObsRow>>

Fetch the merged per-report observations for a station’s window.

Mirrors Python mostlyright.weather.observations(station, from_date, to_date, source=None) — per-report rows only. The strategy option selects one of the concrete fetch paths described at the top of this file.

The returned DataResult carries provenance.source ("merged.live_v1" when unpinned, the bare source when pinned), mirroring Python’s df.attrs["source"]. Every row carries a local_standard_date.

string

ICAO code (e.g. “KNYC”) OR 3-letter NWS code (e.g. “NYC”). Resolved through the station registry: IEM ASOS is fetched by the NWS code, AWC by the ICAO — so observations(“KNYC”) == observations(“NYC”).

string

ISO date YYYY-MM-DD (inclusive)

string

ISO date YYYY-MM-DD (inclusive)

ObsOptions = {}

optional source filter + strategy mode

Promise<DataResult<ObsRow>>

DataResult<ObsRow>: the per-report rows plus provenance

ValidationError when station is malformed or not in the registry (before any fetch — same typed error dailySummaries() throws)

DataAvailabilityError when strategy=‘hosted’ (the hosted ingest API is not available yet)

TypeError when strategy is not in the accepted enum


openMeteoForecasts(station, fromDate, toDate, opts): Promise<DataResult<OpenMeteoRow>>

Fetch Open-Meteo forecasts for station in [fromDate, toDate].

Mirrors Python fetch_open_meteo. Default mode: "training" hits the Previous Runs API; with issuedAt: "..." dispatches to Single Runs API. mode: "live" hits Live Forecast API with cycle-math fallback issuedAt. mode: "seamless" requires allowLeakage: true, and is refused for training data without it.

string

string

string

OpenMeteoOptions = {}

Promise<DataResult<OpenMeteoRow>>


postStormReports(opts): Promise<DataResult<HazardTextProductV1>>

Fetch post-storm reports (PSH) — the per-city damage bulletins.

Mirrors Python mostlyright.weather.hazards.post_storm_reports(...). Same rows, same routes, and the same auto-route on window age as textProducts; the product code is fixed to PSH, because a post-storm report is that code by definition.

Amendments are preserved. product_id keeps its -AAA / -BBB correction suffix, so two amendments of the same report appear as two rows and vintages: "latest" does not collapse them — they are different products, and treating the first as final loses the correction.

PostStormReportsOptions = {}

Promise<DataResult<HazardTextProductV1>>


ratingCurve(gaugeId, opts): Promise<DataResult<HazardRatingCurveV1>>

Return a gauge’s official stage-to-flow curve, whole and ascending.

string

FetchWithRetryOptions = {}

Promise<DataResult<HazardRatingCurveV1>>

Runs in a browser and in Node. Mirrors Python mostlyright.weather.hazards.rating_curve(...) and returns DataResult<HazardRatingCurveV1> stamped "nwps.rating_curve.live", ascending by stage_ft.

This route serves cubic feet per second, which is what makes flow_cfs here directly comparable to a gauge’s *_flow_cfs flood threshold.

The ascending order is established here rather than inherited: the route was measured serving the curve that way, but nothing upstream enforces it, and a curve out of order silently breaks a caller interpolating across it.

ContractError when gaugeId is not addressable, or when the payload reports flow in a unit the shared table does not convert.

NotFoundError when gaugeId addresses no gauge.

const curve = await ratingCurve("ABBG1");
curve.rows.slice(0, 10).map((row) => [row.stage_ft, row.flow_cfs]);

sourceTag(source): LiveSourceTag

Map a validated source name to its canonical row-level identity tag.

"awc" | "iem"

LiveSourceTag


stageAndFlow(gaugeId, opts): Promise<DataResult<HazardStageAndFlowV1>>

Return a gauge’s observed and/or forecast stage-and-flow points.

string

StageAndFlowOptions = {}

Promise<DataResult<HazardStageAndFlowV1>>

With no series the combined document is read and BOTH products come back in one result, discriminated by the series column. With one, only that block is read.

Runs in a browser and in Node. Mirrors Python mostlyright.weather.hazards.stage_and_flow(...) and returns DataResult<HazardStageAndFlowV1> stamped "nwps.stage_and_flow.live", observed rows first.

flow_kcfs is the verbatim upstream number and flow_cfs the converted one: this route serves KILO-cubic-feet per second while the gauge threshold and rating routes serve cubic feet per second, so only flow_cfs is comparable to a *_flow_cfs threshold.

CAPTURE-OR-LOSE: exactly one forecast issuance is ever served and no route parameter asks for a prior one. A gauge with no current forecast answers HTTP 200 with a zero-valued block and yields ZERO rows — fewer than one gauge in four carries a live forecast at any moment, so that is the ordinary answer, not an error.

ContractError when series is not one of the two the route serves, or when gaugeId is not addressable.

NotFoundError when gaugeId addresses no gauge.

const both = await stageAndFlow("ABBG1");
both.rows.filter((row) => row.series === "forecast");

stream(station, opts): AsyncGenerator<LiveObservation>

Yield fresh observations for station from a single source on a polite-floor cadence.

The loop:

  1. Validate source + pollSeconds (throws before the first poll).
  2. Poll once.
  3. If the most-recent observation’s event_time_utc differs from the last one yielded, yield it. Otherwise skip (dedup).
  4. await sleep(pollSeconds).
  5. Loop.

Empty responses (network error, fetcher returned []) do not abort the stream — they’re treated as “nothing fresh yet” and the loop continues after the polite-floor sleep. To get a single-shot failure path, use latest.

string

StreamOptions = {}

AsyncGenerator<LiveObservation>

Error before the first poll when opts.source is unsupported or opts.pollSeconds is below the polite floor.


textProducts(opts): Promise<DataResult<HazardTextProductV1>>

Fetch NWS/NHC text bulletins as rows, with the verbatim text preserved.

Mirrors Python mostlyright.weather.hazards.text_products(...). Returns DataResult<HazardTextProductV1>: snake_case rows plus the provenance envelope whose source is the tag of the leg that answered ("nws.text_products.live" or "iem.afos.archive"). Every row carries the same tag in its own source column and the untouched bulletin in raw_afos, so a better parser can be run over the same bytes later.

Route choice: source: "nws" or "iem" pins a route. Left unset, a fromTime older than the live route’s 7-day retention auto-routes to the Iowa State AFOS archive — a route change, never a rejection.

TextProductsOptions = {}

Promise<DataResult<HazardTextProductV1>>

NoHazardDataError when productId names a product that yields no bulletin text, or when a listed product serves none.

ContractError when the archive route is asked for without a window, or when the window spans more days than the archive walk will issue.


tropicalCyclones(opts): Promise<DataResult<HazardTropicalCycloneV1>>

Fetch the storms NHC currently lists as active.

TropicalCyclonesOptions = {}

Promise<DataResult<HazardTropicalCycloneV1>>

Node-only. www.nhc.noaa.gov sends no Access-Control-Allow-Origin header (measured 1 August 2026); calling this from a browser page will fail CORS, so this verb refuses there with a typed ContractError rather than letting a CORS TypeError surface. A Node process, a Cloudflare Worker, or an extension service worker with www.nhc.noaa.gov in host_permissions can call it.

Mirrors Python mostlyright.weather.hazards.tropical_cyclones(...). Returns DataResult<HazardTropicalCycloneV1> stamped "nhc.tropical_cyclones.live".

A quiet season resolves to an empty result, not an error: upstream serves the activeStorms container unconditionally, so “no storms” is a fact about the world rather than a broken request.


validatePollSeconds(pollSeconds, source): number

Apply the polite-floor invariant to a caller-supplied cadence.

Caller-supplied cadence. undefined/null → use the floor.

undefined | null | number

A validated source name (call validateSource first).

"awc" | "iem"

number

The cadence to use, in seconds.

Error when pollSeconds is below the polite floor.


validateSource(source): "awc" | "iem"

Normalize and validate a source option.

Caller-supplied source string. undefined/null defaults to the first entry in SUPPORTED_SOURCES (AWC). Case-insensitive.

undefined | null | string

"awc" | "iem"

The normalized lowercase source name (one of SUPPORTED_SOURCES).

Error when the source is not in SUPPORTED_SOURCES.


waterObservations(gaugeId, fromTime, toTime, opts): Promise<DataResult<HazardWaterObservationV1>>

Return USGS continuous water values for one site over one window.

string

A USGS site number in either spelling: the bare "02215260" the water-prediction service carries, or the "USGS-02215260" this API addresses. Both canonicalize to the BARE number, which is what every row’s gauge_id carries — so one reading is never stored under two identities. Anything that is not eight to fifteen digits throws before any request.

Date

Window start.

Date

Window end, at or after fromTime.

WaterObservationsOptions = {}

Promise<DataResult<HazardWaterObservationV1>>

Resolves the site ONCE — one monitoring-locations request per call, never one per page — to validate it and to read the standard-time abbreviation and daylight-saving flag every returned row is stamped with, then walks the continuous collection over the window.

Runs in a browser and in Node. Mirrors Python mostlyright.weather.hazards.water_observations(...) and returns DataResult<HazardWaterObservationV1> stamped "usgs.waterdata.live". A VALIDATED site that recorded nothing in the window returns an EMPTY result; an unknown site throws.

USGS marks recent values Provisional and flips them to Approved roughly 60 to 90 days later. That flip can change the VALUE, not only the label, and it republishes the same instant under a newer last_modified_at_utc — so a re-fetch of an unrevised row is idempotent while a revision APPENDS beside the earlier reading. This is the only flood product whose vintage comes from upstream.

ContractError when the window is inverted, when approvalStatus is not a state USGS publishes, or when the site number is unusable.

NotFoundError when gaugeId addresses no monitoring location.

const day = await waterObservations(
"02215260",
new Date(Date.UTC(2026, 7, 1)),
new Date(Date.UTC(2026, 7, 2)),
);
day.rows.map((row) => [row.valid_at_utc, row.value, row.approval_status]);

wildfires(opts): Promise<DataResult<HazardWildfireV1>>

Return wildfire incident records — national interagency, or California state.

WildfiresOptions = {}

Promise<DataResult<HazardWildfireV1>>

With no arguments this is the current national wildfire picture: the interagency current service, wildfires only, complex children excluded, every row stamped "wfigs.current.live".

BROWSER + NODE on the default leg. wildfires({ source: "calfire" }) is NODE-ONLY — incidents.fire.ca.gov sends no Access-Control-Allow-Origin header (measured 1 August 2026), so a browser page is refused at call time with a typed ContractError rather than an opaque CORS failure. See docs/browser-integration.md.

Mirrors Python mostlyright.weather.hazards.wildfires(...) and returns DataResult<HazardWildfireV1> tagged with the leg that answered: "wfigs.current.live", "wfigs.season.live", "wfigs.history", or "calfire.incidents.live".

The interagency season and history windows are the operational record: the publisher states that they represent “a snapshot in time of what was known for each incident during the operational period of the fire” and are “not QA/QC’d after a fire is declared out”. The certified record lives on a different service, which this tier catalogs and does not read.

persist is Python-only; see ./wildfires.types.ts.

ContractError when source is unknown, when an argument belongs to the other leg (the message names that leg), when a value would reach the query clause in an unchecked shape, or when a history walk exceeds WFIGS_HISTORY_ROW_CEILING — measured on every history call, bounded or not, because a date bound is not a size.

HazardRetentionError when fromDate or toDate predates what scope: "current" can answer — a window is historical when either end of it is. It never returns a short frame.

SourceUnavailableError when a service answers HTTP 200 with an error object, or when the state route answers a season with another season’s rows.

ApiKeyRequiredError when no API key is configured for catalog resolution.

const burning = await wildfires();
burning.rows.map((row) => [row.incident_name, row.acres, row.percent_contained]);
const california = await wildfires({ source: "calfire", year: 2025 });