A sync + async client inspired by blsR for the U.S. Bureau of Labor Statistics
(BLS) Public Data API.
- Sync and async clients (
BLSClient,AsyncBLSClient). - Polars output returns easy to use and convert Polars Dataframes.
- Automatic batching over the BLS per-request limits (series count and year span).
Requires Python 3.11+:
pip install bls-api-client
# or
uv add bls-api-clientimport blsapifrom blsapi import BLSClient
with BLSClient() as client:
df = client.get_series("LNS14000000", start_year=2023, end_year=2024)
print(df)The result is a Polars DataFrame. Exporting elsewhere is simple:
df.write_parquet("data.parquet")
df.write_csv("data.csv")
df.to_pandas()
df.to_arrow()import anyio
from blsapi import AsyncBLSClient
async def main():
async with AsyncBLSClient() as client:
df = await client.get_series("LNS14000000", start_year=2023, end_year=2024)
print(df)
anyio.run(main)Everything has a sensible default, so bls-api-client works out of the box.
A registration key is optional but raises your rate limits substantially. The key is resolved with this precedence:
- The explicit
api_key=argument, if given. - Otherwise the
BLS_API_KEYenvironment variable. - Otherwise no key (unauthenticated tier).
# Explicit (highest precedence)
client = BLSClient(api_key="API_KEY")
# Or rely on the environment
# export BLS_API_KEY=API_KEY (macOS/Linux)
# $env:BLS_API_KEY = "API_KEY" (PowerShell)
client = BLSClient()
client.has_key # returns True if a key is activeAll are keyword-only on both clients:
| Argument | Default | Purpose |
|---|---|---|
api_key |
None -> BLS_API_KEY |
Registration key. |
base_url |
https://api.bls.gov/publicAPI/v2/ |
API base URL. |
timeout |
5/30/10/5s (connect/read/write/pool) |
httpx2.Timeout. |
max_retries |
3 |
Application-level retries for transient failures. |
auto_batch |
True |
Transparently split over the tier limits. |
user_agent |
blsapi/<version> |
Sent as User-Agent. |
client |
None |
Inject your own httpx2.Client/AsyncClient. |
import httpx2
from blsapi import BLSClient
client = BLSClient(
api_key="API_KEY",
max_retries=5,
timeout=httpx2.Timeout(connect=5.0, read=60.0, write=10.0, pool=5.0),
user_agent="app/1.0 (you@example.com)",
)Pass a list of ids, or a {label: id} mapping to relabel the output's series_id column:
df = client.get_series(
{"unemployment": "LNS14000000", "cpi": "CUUR0000SA0"},
start_year=2020,
end_year=2024,
)
df["series_id"].unique().to_list() # ["cpi", "unemployment"]Reshape the long frame to one value column per series, indexed by date:
from blsapi import pivot_wide
wide = pivot_wide(df) # columns: date, unemployment, cpi(Annual-average rows have no real date and are dropped by pivot_wide.)
df = client.get_series(
"LNS14000000", start_year=2023, end_year=2024, calculations=True
)
# adds net_change_{1,3,6,12}m and pct_change_{1,3,6,12}m columns, as provided by the BLS API.
resp = client.get_series_raw("LNS14000000", catalog=True) # returns a BLSResponse with catalog metadataclient.list_surveys() # returns a Polars DataFrame of all surveys
client.get_survey("CU") # returns a dict of metadata for one survey
client.get_popular() # returns list of popular series ids
client.get_popular("LN") # returns popular ids within a surveyclient.query_cost(["LNS14000000", "CUUR0000SA0"], 2000, 2024) # returns the number of API calls usedAll errors derive from BLSError, so you can catch broadly or narrowly:
from blsapi import BLSError, BLSValidationError, BLSAPIError, BLSHTTPError
try:
df = client.get_series("BAD_ID", start_year=2024, end_year=2023)
except BLSValidationError:
... # bad input caught locally, before any network call
except BLSAPIError as e:
... # BLS returned a non-success status; inspect e.status and e.messages
except BLSHTTPError:
... # transport/HTTP failure that persisted through the retry loop
except BLSError:
... # anything else from this library| Series per request | Years per request | Queries per day | |
|---|---|---|---|
| No key | 25 | 10 | 25 |
| With key | 50 | 20 | 500 |
With auto_batch=True (the default), requests that exceed the per-request limits are split
into multiple calls automatically and stitched back together. Register for a free key at
https://data.bls.gov/registrationEngine/.
uv sync # install dependencies
uv build # build the sdist + wheel