Obscura

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.

Category Macro Columns 9 Refresh Weekly Entity key series_id Point-in-time available_date

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.

ColumnTypeDescription
series_idtext · not nullBLS 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.
yearinteger · not nullReference 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.
periodtext · not nullBLS 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_nametextBLS'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.
valuedouble precisionThe 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_codetextBLS 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_texttextHuman-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_enddateThe 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_datedatePUBLIC-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.

Related datasets

Comtrade FlowsAnnual bilateral goods-trade totals between countries, from UN Comtrade. ECB SeriesEuropean Central Bank Data Portal series, retrieved by series key. FRED SeriesSeries from the Federal Reserve's FRED database by series ID — rates, prices, output, employment and more — as a point-i… IMF IndicatorsCountry-level macro indicators from the IMF's DataMapper. ONS SeriesUK macroeconomic series from the Office for National Statistics. Policy RatesThe policy interest rate set by each currency's monetary authority, on every calendar day back to 1946 — twenty currenci… Treasury Average RatesAverage interest rates the U.S. Treasury pays on its outstanding debt, by security type. Treasury DebtTotal U.S. public debt outstanding, updated daily ('debt to the penny').