TherminalClient
MostlyRightClient (aliased as TherminalClient) is the main client. It covers everything except live METAR — for that, use WeatherLive.
from mostlyright import MostlyRightClientclient = MostlyRightClient()01 · Construction
Section titled “01 · Construction”MostlyRightClient( api_key: str | None = None, # defaults to MOSTLYRIGHT_API_KEY env var base_url: str | None = None, # defaults to https://api.mostlyright.md timeout: float = 30.0, # per-request timeout in seconds)Use as a context manager to guarantee the HTTP session closes:
with MostlyRightClient() as client: obs = client.observations("NYC")02 · observations(): the main method
Section titled “02 · observations(): the main method”Returns raw hourly weather observations.
client.observations( station: str | None = None, from_date: str | None = None, to_date: str | None = None, *, as_of: str | None = None, obs_type: str | None = None, resolution: str | None = None, units: str | None = None, tz: str | None = None, format: str | None = None, columns: list[str] | None = None, features: list[str] | None = None, limit: int = 1000, offset: int = 0, as_dataframe: bool = False, save_path: str | Path | None = None,) -> list[dict] | DataFrame | strRequired-ish parameters
Section titled “Required-ish parameters”station— 3-letter NWS code (e.g."NYC","ATL"). Required.from_date/to_date—YYYY-MM-DD. Inclusive on both ends.
Settlement-critical parameters
Section titled “Settlement-critical parameters”-
as_of— UTC ISO-8601 timestamp. Filters toobserved_at <= as_of. Use this for point-in-time correctness in inference loops and backtests. Example:"2026-04-12T14:00:00Z".# What was the NYC temp known as of noon UTC, April 12?obs = client.observations("NYC", as_of="2026-04-12T12:00:00Z")as_ofmust be UTC. Naive timestamps raiseValidationError. Comparison is on datetime objects, not strings, so timezone conversions are safe. -
obs_type— filter by report type."METAR"(hourly) or"SPECI"(special, off-hour reports for significant weather). Omit to get both.
Shape parameters
Section titled “Shape parameters”-
columns— list of field names to return. Reduces payload size.obs = client.observations("NYC", columns=["observed_at", "temp_f", "wind_speed_kt"]) -
format—"json"(default),"csv","parquet", or"toon"."toon"is a compact tabular encoding optimized for AI agent context windows. -
as_dataframe— return a pandas DataFrame instead of a list of dicts. Requirespandasinstalled. -
save_path— write the response to disk. Format inferred from extension (.csv,.parquet,.json).
Feature DSL
Section titled “Feature DSL”-
features— list of derived feature spec strings evaluated client-side from the raw observations.obs = client.observations("NYC",from_date="2026-04-01",to_date="2026-04-07",features=["temp_f.rolling_mean(24)", "wind_speed_kt.pct_change(1)"],as_dataframe=True,)See
client.feature_catalog()for every supported field and available transforms.
Paging
Section titled “Paging”limit— default 1000, max 10,000.offset— skip N rows. Use in combination withlimitfor cursor-style paging. For time-series slicing, preferfrom_date/to_dateoveroffset.
03 · Other methods
Section titled “03 · Other methods”climate(station, from_date, to_date, ...)
Section titled “climate(station, from_date, to_date, ...)”Daily climate aggregates from GHCNd. Different entity from observations — aggregated, not raw.
climate_gaps(station, from_date, to_date)
Section titled “climate_gaps(station, from_date, to_date)”Identifies gaps in the climate record for a station. Useful for sanity-checking a backfill before trading on it.
pairs(station, horizons=[6, 12, 24], ...)
Section titled “pairs(station, horizons=[6, 12, 24], ...)”Joins forecasts to ground-truth observations at settlement. This is the dataset you train models against.
snapshot(station, as_of)
Section titled “snapshot(station, as_of)”Point-in-time view of one station at a given UTC moment. Temporally honest: observations are filtered to [window_start_utc, as_of], climate is withheld until the NWS CLI publication threshold has passed. Useful for dashboard polling and for auditing “what did the market know at time T?”
forecasts(station, model, ...) and forecast_series(...)
Section titled “forecasts(station, model, ...) and forecast_series(...)”Forecast ingestion from NWS MOS and Open-Meteo. Returns the forecast record as issued, keyed by issued_at.
feature_catalog()
Section titled “feature_catalog()”Returns every supported observation field — type, unit, bounds, and catalog of transforms available in the features parameter.
stations() and station(code)
Section titled “stations() and station(code)”List all stations or fetch one by code. StationInfo includes lat/lon, timezone, network, and ICAO equivalent.
availability(station, from_date, to_date)
Section titled “availability(station, from_date, to_date)”Reports data-density for a station over a range. Use to decide whether a station has enough coverage for your backtest window.
capabilities()
Section titled “capabilities()”Runtime endpoint — returns rate limits, supported formats, SDK version compatibility. Treat as the source of truth over anything in this doc.
data_version(station, ...)
Section titled “data_version(station, ...)”Deterministic version token for a station+range. Use to cache or to assert “the data I’m training on is the same data I traded on.”
04 · Common pitfalls
Section titled “04 · Common pitfalls”Timezones
Section titled “Timezones”All observed_at and as_of timestamps are UTC. Passing a naive or local timestamp for as_of raises ValidationError. Convert explicitly:
from datetime import datetime, timezonelocal = datetime(2026, 4, 12, 8, 0)utc = local.astimezone(timezone.utc).isoformat()# Use utc as the as_of value.Station codes
Section titled “Station codes”Use 3-letter NWS codes ("NYC") for the station parameter on observations(), climate(), forecasts(), snapshot(), and pairs(). An unknown code returns 404. Call client.stations() to get the canonical list.
The /stations/{code} metadata endpoint accepts either 3-letter NWS ("NYC") or 4-letter ICAO ("KNYC"), case-insensitive. Elsewhere, 3-letter NWS only.
Empty responses
Section titled “Empty responses”An empty list isn’t an error. It means no observations in that range — gaps happen, especially at secondary stations. Use availability() to confirm whether the gap is real or a query bug.
as_of without to_date
Section titled “as_of without to_date”If you pass as_of without to_date, today’s date is used as the API partition bound and as_of is applied as a post-filter. For large ranges, pass both — you’ll pull less data over the wire.
See also
Section titled “See also”- WeatherLive — real-time METAR.
- API Reference — the raw HTTP surface.
- Data sources — why source priority matters for
as_of.