Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bls-api-client

PyPI version Python versions License: MIT

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).

Installation

Requires Python 3.11+:

pip install bls-api-client
# or
uv add bls-api-client
import blsapi

Quick start

from 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()

Async

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)

Configuration

Everything has a sensible default, so bls-api-client works out of the box.

API key

A registration key is optional but raises your rate limits substantially. The key is resolved with this precedence:

  1. The explicit api_key= argument, if given.
  2. Otherwise the BLS_API_KEY environment variable.
  3. Otherwise no key (unauthenticated tier).

Example:

# 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 active

Other options

All 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)",
)

Usage

Multiple series and named aliases

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"]

Wide format

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.)

Calculations and catalog

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 metadata

Surveys and popular series

client.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 survey

Quota planning

client.query_cost(["LNS14000000", "CUUR0000SA0"], 2000, 2024)  # returns the number of API calls used

Error handling

All 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

Tiers & limits

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/.

Development

uv sync          # install dependencies
uv build         # build the sdist + wheel

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages