humanbaselines
Python client for the Human Crash Baselines API: typed access to human-driver crash-rate baselines by region, route, and filter.
It wraps the /v1 REST API: construct a client with your key, call typed methods, get typed results back (no hand-built JSON, no remembering the auth header).
Install
pip install humanbaselines
Or install the latest from source:
pip install "git+https://github.com/valgorithmic/humanbaselines.git"
# or, from a checkout:
pip install .
Quickstart
from humanbaselines import HumanBaselines
hb = HumanBaselines(api_key="hbk_...") # or set HUMANBASELINES_API_KEY
# Geofence crash rate (kwargs are validated client-side):
r = hb.compute(region="travis", outcome="police_reported", ego_vehicle=["cars", "light_trucks"])
print(r.rate, r.rate_low, r.rate_high) # 4.055 4.0 4.1
print(r.N, r.D_miles, len(r.cells)) # 24617.0 6.07e9 1795
# Discover valid filters + defaults at runtime:
for f in hb.filters().modes["geofence"]:
print(f.id, "→", [o.id for o in f.options], "default:", f.default)
# Which regions / modes are available:
hb.regions()
A region is one served area: a county (travis), a group of them
(sf is San Francisco, San Mateo and Santa Clara), a municipality
(boston, cambridge, worcester), or a multi-state corridor
(interstates). hb.regions() is the live list — it grows.
region used to be called county, which was wrong for most of those.
The old name still works everywhere it did before: county= on every
compute call, counties= on compute_batch, and "county" in a saved
config. Both go on the wire, so a pinned older client and a current one
behave the same.
Filters
Pass filters three ways - keyword args (simplest), a typed Selections model,
or a plain dict. All are validated before the request, so a bad value fails
fast locally:
from humanbaselines import GeofenceSelections, Outcome
hb.compute(outcome="fatal", road_type=["interstate"]) # kwargs
hb.compute(selections=GeofenceSelections(outcome=Outcome.fatal)) # typed model
hb.compute(selections={"outcome": "fatal"}) # dict
Omitted filters fall back to the API's defaults. Two are worth knowing, because they are the defaults rather than the widest setting:
in_transport="in_transport"drops vehicles that were parked when struck.desk_reports="exclude"drops crashes the driver reported at a police station instead of an officer attending. Only a source that marks the channel has any to drop, which today means Chicago, and excluding them is what makes its rate mean the same thing as another region's. Passdesk_reports="include_all"to count them, and expect that region's numbers to jump.
posted_speed filters by the posted speed limit, and applies to crashes and
miles alike. Most operating domains state it as a cap, so pass one in mph:
hb.compute(region="sf", posted_speed_max=45) # roads posted at 45 mph or less
The cap must be one of the 5 mph steps from 15 to 65. It expands client-side
into the run of bands the API takes (le15, s20, ... s45), which you can
also pass directly as posted_speed=[...] for a window that is not anchored at
the bottom. The lowest band is an open tail, so a cap of 15 covers anything
posted below 15 too. It is built for sf and vegas. In every other region any
selection is a no-op and returns the unfiltered rate, so check the region before
quoting a per-speed number.
Validation is client-side, so a filter the installed client predates is rejected
locally even when the API supports it. Upgrade the package if a filter you can
see in hb.filters() is refused here.
Binding a baseline definition
The filter selections are really a methodological definition - what counts as a crash and what you're baselining against. Bind that definition to the client once and every call inherits it; per-call args override individual fields:
hb = HumanBaselines(api_key="hbk_...", config={
"region": "travis",
"outcome": "fatal",
"ego_vehicle": ["cars", "light_trucks"],
})
hb.compute().rate # uses the bound definition
hb.compute(weather=["rain"]).rate # override just one field for this call
hb.config() # full effective config (bound + every default)
hb.changes() # only the settings that differ from the defaults
config(mode="geofence") and changes(mode="geofence") take an optional mode
(each mode exposes different fields). config() is the complete definition a
compute call would use; changes() is just your deviations from the defaults.
Only the fields you set are sent. The server fills every other field with the
region's own default, which can differ from the tool-wide one shown by
config(). For example, crash_year defaults to 2023 in Boston and the Florida
regions, since Florida's crash data starts in 2023. Pass crash_year explicitly
when you need a specific year.
The bound config is validated when you create the client (unknown fields or bad
values raise immediately). It applies to all modes - each compute mode uses
the subset of fields it understands (e.g. road_type only affects geofence,
ci_method only affects route/depot), so you can keep one definition across
modes.
Derive variants immutably, and version definitions as JSON:
strict = hb.with_config(under_reporting="adjusted") # new client; hb is unchanged
hb.save_config("odd_fatal_cars.json") # full config snapshot (check into a repo, share, diff)
# Load it back - pass the path straight to the constructor. Values equal to the
# tool-wide default are not re-sent, so region defaults still apply.
hb2 = HumanBaselines(api_key="hbk_...", config="odd_fatal_cars.json")
# (HumanBaselines.from_config(path, api_key=...) is an equivalent, explicit alias.)
Route & depot modes
Available for route/depot-capable regions - interstates
(check hb.regions()). Route/depot count Class-8 combination
trucks, so ego_vehicle defaults to ["combination"] in these modes.
hb.compute_route(segment_ids=[("I-35", 250), ("I-35", 251)], ego_vehicle=["combination"])
hb.compute_depot_route(
depot_a=(30.25, -97.75), # (lat, lon)
depot_b=(30.40, -97.70),
ci_method="fay_feuer",
)
Errors
Non-2xx responses raise typed exceptions you can catch:
from humanbaselines import AuthenticationError, ValidationError, ServiceUnavailableError
try:
hb.compute(outcome="fatal")
except AuthenticationError: # 401 - bad/missing key
...
except ValidationError as e: # 422 - e.errors has the field-level detail
print(e.errors)
except ServiceUnavailableError: # 503 - service warming up (auto-retried first)
...
All inherit from HumanBaselinesError. The base APIError carries .status_code
and .body.
An outcome a region's source data cannot measure raises APIError with status
400, and the message says why. The airbag outcomes are unavailable in the
Massachusetts regions, DC, the Iowa counties and Denver. Tokyo serves neither
the police-reported nor the serious-injury (ka) tier. The value is valid everywhere, so this is the
server's answer rather than a client-side check.
Configuration
| arg | default | notes |
|---|---|---|
api_key |
$HUMANBASELINES_API_KEY |
sent as X-API-Key |
base_url |
https://humanbaselines.com |
proxies /v1/*; use the Cloud Run URL for /health |
timeout |
30 |
seconds per request |
max_retries |
2 |
exponential backoff on 502/503/504 (handles cold-start warm-up) |
HumanBaselines is also a context manager (with HumanBaselines(...) as hb:).
Notes
- The typed models in
_generated.pyare generated from the server's OpenAPI schema - the server's Pydantic models are the single source of truth, so the client can't drift from the API.GET /v1/filtersis the authoritative runtime source for valid values and defaults. - Interactive API docs: the
/docspage on the API host. - Batch results are keyed by region. Until the models are regenerated against a
deployed API that emits it, read
.countyon aBatchItemResult; the server sends both.
Development
pip install -e '.[dev]'
pytest -q # mocked tests
HUMANBASELINES_API_KEY=hbk_... pytest -q # also runs the live smoke test
python -m build # build wheel + sdist into dist/
Regenerating models after an API change
src/humanbaselines/_generated.py is generated - never hand-edit it. When the
API contract changes, regenerate from the live OpenAPI schema:
python scripts/regenerate_models.py
# point at a different host (e.g. local dev server):
python scripts/regenerate_models.py --url http://localhost:8000
This fetches <host>/openapi.json (default https://humanbaselines.com) and runs
datamodel-code-generator. Output is deterministic (no timestamps), so a clean
git diff means the client is in sync with the API.
Releases are automated: a version bump in the upstream app dispatches a release here, which tags
v<version>and publishes to PyPI via Trusted Publishing.
License
Apache-2.0 © Valgorithmic, Inc. (d.b.a. Valgo)
Metadata
Release files for humanbaselines 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| humanbaselines-0.3.0.tar.gz | 29.2 kB | With provenance | Details for humanbaselines-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| humanbaselines-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for humanbaselines-0.3.0-py3-none-any.whl |
Total release size: 52.6 kB
Release files / humanbaselines-0.3.0.tar.gz
| Download URL | humanbaselines-0.3.0.tar.gz |
|---|---|
| Size | 29.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
008e9b80ecd8b576a0beab59a81949fa18b4741d3e9e141d9f365176696ee78d
|
|
BLAKE2b-256 checksum How to use checksums |
9e438802aad7d2518eb2807666f0c7043adb45178c80173955145de477d00de4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / humanbaselines-0.3.0-py3-none-any.whl
| Download URL | humanbaselines-0.3.0-py3-none-any.whl |
|---|---|
| Size | 23.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a61acd6f16d54b2d3c9cbed97b6352611652ef2041d47628ba8510e7a3362bf5
|
|
BLAKE2b-256 checksum How to use checksums |
ddeabc65c682b0bfeb6098d6a50751c580bae804312b8a9a35e42f503077a76e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logSecurity for humanbaselines 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for humanbaselines-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.