Changelog¶
All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]¶
[0.1.0a4] - 2026-08-04¶
Added¶
Client:
client.calenviroscreen5— CalEnviroScreen 5.0 census tract scores (list(**params),get(tract)). Single-vintage dataset, noyearfilter.CLI:
sjvair calenviroscreen5— CalEnviroScreen 5.0 export.Client:
client.calheatscore— CalEPA CalHeatScore daily ZIP-code heat-risk scores (list(**params),zipcode(zipcode, **params)).CLI:
sjvair calheatscore— CalHeatScore export, with--zipand--dateflags.Client:
client.monitors.closest(),.current(), and.current_at()now accept**params(e.g.device='CIMIS'), matchinglist()’s existing filter passthrough — the backend now honors?device=on these endpoints.CLI:
--deviceflag onsjvair monitors closestandsjvair monitors current.Client:
client.forecasts— SJVAPCD daily air quality forecasts by SJV county zone (list(**params),get(forecast_id)).CLI:
sjvair forecasts— SJVAPCD forecast export, with--date,--issued-date, and the standard region flags.Client:
client.tempo— NASA TEMPO satellite air-quality data (products(),granules(product, **params),latest(product),point(product, latitude, longitude, start=None, end=None),region(product, region_id, start=None, end=None)).CLI:
sjvair tempo --type {products,granules,latest,point,region}.Client:
ClientError— raised on non-retryable HTTP 4xx responses other than 404/429 (which keep their ownNotFound/RateLimitedtypes), instead of leaking a rawrequests.HTTPError.CLI:
--workersflag onsjvair monitors summaries— fetches each monitor’s summaries concurrently instead of one at a time.
Changed¶
Breaking:
client.calenviroscreenis replaced byclient.calenviroscreen4(CES4) andclient.calenviroscreen5(CES5) — there’s no bare/default version, so a future CES6 doesn’t have to fight over what the short name means.CalEnviroScreen4Resource.get()’s argument order changes fromget(year, tract)toget(tract, year=None)now thatyearis optional, matching the backend, which now defaults it server-side to 2020 instead of requiring it in the URL path.Breaking:
sjvair calenviroscreenis replaced bysjvair calenviroscreen4andsjvair calenviroscreen5.--yearis now optional oncalenviroscreen4(was required).
Fixed¶
format_output(..., 'dataframe')/'geodataframe'(and the CLI’s--format dataframe/--format geodataframe, once wired up) always raisedTypeError—dtype_backendisn’t a validpandas.DataFrame()constructor argument. Now applied via.convert_dtypes(dtype_backend=...)instead.SJVAirClient.get()didn’t retry transient network failures (ConnectionError/Timeout) at all — only HTTP 429/5xx responses were retried. Network errors now retry with the same exponential backoff as 5xx, raisingServerErroronce exhausted.Concurrent 429 responses could race in
CooldownGate: a thread with a shorterRetry-Aftercould reopen the gate before a concurrently-running, longer cooldown had actually finished. Cooldowns now only ever extend the shared deadline, never shorten it.map create/timelapse createraised a rawKeyErrorfor an unknown--typeinstead of a clear error listing valid entry types.
[0.1.0a3] - 2026-07-09¶
Added¶
CLI: global
--tz/SJVAIR_TZ(IANA zone name, e.g.America/Los_Angeles) localizes naive timestamps passed tomap create --timestampandtimelapse create --start/--endbefore they’re sent to the API. An explicit UTC offset in the timestamp always wins over--tz; with neither, naive timestamps are still treated as UTC (unchanged default).CLI:
--location {inside,outside}onmap create/timelapse createfilters to indoor or outdoor monitors. Filtered client-side, since neither the livecurrent/nor historicalat/endpoint supports a location query filter server-side.CLI:
map create/timelapse creategain the same--county/--city/--zip/--tract/--urbanregion-filter shortcuts already available on the other data-export commands, resolved by type (e.g.--urban Fresnocan’t accidentally match the county or city of the same name).
Changed¶
CLI: commands with no
--formatand no--output(or an--outputwith an unrecognized extension) now print CSV instead of JSON — CSV is the more common target for a download-focused CLI. Pass--format jsonfor the old behavior.
Fixed¶
sjvair.maps.render_frame()markers now use a border that’s a darker shade of their own fill color (_blend_hex(fill, '#000000', 0.2), matching sjvair.com’s own region-admin rendering) instead of plain black.sjvair.maps.shape_for_monitor()now classifies regulatory (triangle) monitors by the API’sgradefield (fem/frm) instead of a hardcoded, already-broken set of monitortypestrings (the old set used mixed-case names like'AirNow'that never matched the API’s actual lowercase values, so every monitor rendered as a circle or square regardless of grade). Requires a server exposinggradeon/monitors/.
[0.1.0a2] - 2026-07-08¶
Added¶
CLI:
map create/timelapse create— render static map images and timelapse videos, live or as of a historical timestamp, scoped by region/bbox/buffer. Requires the optionalsjvair[maps]extra (andffmpegfor timelapses).MonitorsResource.current_at()— likecurrent(), but as of a historical timestamp; backsmap create/timelapse createand is usable directly.sjvair.maps— standalone map-rendering module (render_frame,color_for_value,shape_for_monitor), importable without the optional dependencies; only rendering itself requiressjvair[maps].
Fixed¶
sjvair.maps.color_for_value()no longer crashes on monitors whoselatest.valuethe API serializes as a JSON string (server-sideDecimalfields aren’t natively JSON-serializable) rather than a number.
[0.1.0a1] - 2026-07-02¶
Added¶
Python client (
SJVAirClient) — read-only access to the SJVAir API with configurable base URL, API key, and timeout; retry with backoff; request cooldown; context-manager lifecycle; and lazy pagination.Resources —
monitors,regions,calenviroscreen(CalEnviroScreen 4.0),ceidars,hms(smoke and fire), andpesticides.CLI (
sjvair) — download-focused command-line tool:monitors:list,get,entries,summaries,current,closestregions:list,get,summariescalenviroscreen,ceidars,hms(smoke/fire),pesticidesShared region filters (
--county,--city,--zip,--tract,--urban,--region-id); comma-separated or repeated--monitor-idOutput as CSV, JSON, or YAML (inferred from the output extension or
--format)Global
--api-key,--base-url,--timeout,--quiet,--force
Bulk export (
ExportEngine,sjvair monitors entries) — chunked, concurrent downloads that stay within the server’s 180-day export limit, with NDJSON staging that resumes interrupted runs and rolls up into a single CSV or JSON file.Output formats (
format_output) —objects,tabular,dataframe, andgeodataframe, the last two via the optionalsjvair[maps]extra.Typed package (ships
py.typed); supports Python 3.10 through 3.14.