BLS Series
Economic series from the U.S. Bureau of Labor Statistics, fetched by series ID: employment, unemployment, CPI, PPI, wages and productivity.
bls_series — the dataset name to pass to the Obscura API.
What one row means
One BLS series observation for a single reference period: a (series_id, year, period) triple carrying the reported value and BLS's own human-readable period label, as returned by the keyless public API v2 timeseries endpoint in long format.
One row per (series_id, year, period).
Point-in-time availability
Every Obscura dataset carries available_date: the calendar day the publisher made the row
available, day-of, with no session rounding. It is the one column a backtest filters on, and it means the
same thing on every dataset in the catalog.
For bls_series: published: available_date = period_end (derived from year+period: M01-M12 month-end, M13 = Dec 31, Q01-Q04 quarter-end) + the program's ~21-day dissemination lag, the BLS release-calendar date for that observation.
Refresh cadence
Obscura refreshes bls_series weekly — the most frequent scheduled job that re-collects or re-exports it. This is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when a row became public is recorded per row in available_date.
Schema — 9 columns
The full public column list for bls_series, with the meaning of every field. The same
schema is served unauthenticated at https://api.obscura.trade/v1/catalog/bls_series.
| Column | Type | Description |
|---|---|---|
| series_id | text · not null | BLS series identifier, a fixed-width code encoding survey/seasonal-adjustment/category (e.g. "CES0000000001" = All Employees, Total Nonfarm, SA; "LNS14000000" = civilian unemployment rate, SA; "CUUR0000SA0" = CPI-U, All Items, NSA). Set verbatim from the v2 API response's `seriesID` field; one of the ids the task was configured with via BlsArgs.series. Part of the composite primary key. |
| year | integer · not null | Reference year of the observation (API's `year` field as an integer), e.g. 2026. Not itself a date — combined with `period` it identifies the reference period. The task requests years [end_year - years_back + 1 .. end_year] per BlsArgs.years_back (default 3). Part of the composite primary key. |
| period | text · not null | BLS period code within `year`, verbatim from the API's `period` field: M01-M12 = calendar month, M13 = annual average, Q01-Q04 = calendar quarter. Determines what period_end resolves to. Part of the composite primary key. |
| period_name | text | BLS's human-readable label for `period` (e.g. "January", "Annual", "1st Quarter"), from the API's `periodName` field. Descriptive only, nullable, updated on every upsert; not used for any date derivation. |
| value | double precision | The reported observation value, parsed from the API's string `value` field by parse_bls; non-numeric BLS markers such as "-" (suppressed, not-yet-released, or not-applicable) are parsed to NULL rather than 0. Units are series-specific and NOT standardized in this table — index points (e.g. CPI), a percent rate (unemployment), thousands of persons (CES employment level), dollars (average hourly earnings), etc. — the unit must be looked up per series_id against BLS's own series-definition catalog; there is no unit column here. |
| footnote_code | text | BLS footnote CODE for this observation, from the first annotated entry of the API's `data[].footnotes` array (`footnotes[].code`) — the machine REASON for the value, NOT derivable from `value` (which is NULL for every non-numeric marker, collapsing the distinctions): e.g. "9" = data unavailable due to a lapse in appropriations (distinct from an ordinary suppression), "12" = the value was revised to incorporate updated population controls. NULL when the observation carries no footnote. A data-quality / revision-status flag. |
| footnote_text | text | Human-readable BLS footnote TEXT paired with `footnote_code`, from the same `data[].footnotes[].text` entry (e.g. "Data unavailable due to the 2025 lapse in appropriations."). Descriptive gloss on the code; NULL when there is no footnote. |
| period_end | date | The reference period's calendar end date, derived from year+period: M01-M12 resolve to that calendar month's last day, M13 to December 31 of year, Q01-Q04 to that calendar quarter's last day. DB-generated (STORED), read-only; the period/event date this observation covers — not when it became public. |
| available_date | date | PUBLIC-availability date = period_end + the program's declared dissemination lag (~21 days), BLS's actual release-calendar date for this observation. DB-generated (STORED), read-only; the point-in-time column to filter/join on — NEVER the reference period itself. |
Access bls_series
Two delivery paths, one identifier. Both require an Obscura account and an active subscription; the catalog entry and the schema above are public.
import obscura
client = obscura.Client("obs_live_…")
df = client.query(
dataset="bls_series",
symbols=["NVDA", "AAPL"],
start="2024-01-01",
)
Create a free account Browse all 95 datasets
Frequently asked questions
What is in the bls_series dataset?
Economic series from the U.S. Bureau of Labor Statistics, fetched by series ID: employment, unemployment, CPI, PPI, wages and productivity. One BLS series observation for a single reference period: a (series_id, year, period) triple carrying the reported value and BLS's own human-readable period label, as returned by the keyless public API v2 timeseries endpoint in long format.
How do I avoid look-ahead bias with bls_series?
Filter on bls_series.available_date, the day the publisher made the row public. For this dataset that date is derived as follows — published: available_date = period_end (derived from year+period: M01-M12 month-end, M13 = Dec 31, Q01-Q04 quarter-end) + the program's ~21-day dissemination lag, the BLS release-calendar date for that observation. A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.
In what formats can I get bls_series?
As a Parquet bulk export (POST https://api.obscura.trade/v1/download) or as JSON from the typed query API (POST https://api.obscura.trade/v1/query), both with dataset="bls_series". The column schema is public at https://api.obscura.trade/v1/catalog/bls_series.
Can I filter bls_series by company or symbol?
Yes. bls_series carries series_id, the column the API's symbols filter resolves against.
How often is bls_series updated?
Obscura refreshes bls_series on a weekly schedule — that is the most frequent scheduled job that re-collects or re-exports the table. It is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when the publisher makes a row available is described by the availability rule above, and is recorded per row in available_date.