Obscura

ONS Series

UK macroeconomic series from the Office for National Statistics.

ons_series — the dataset name to pass to the Obscura API.

Category Macro Columns 14 Refresh Daily Entity key series_id Point-in-time available_date

What one row means

One published data point for a single curated UK ONS macro series (CPI/CPIH/RPI inflation indices or labour-market rates) for one reference period — e.g. the CPI 12-month rate for May-2026, or the unemployment rate for the March-May 2026 rolling window.

One row per (series_id, period_end).

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 ons_series: published: available_date is the point's OWN publication day, materialized by the collector from revised_at (raw months[].updateDate) on ONS's London clock — m20260702_000183 replaced the generated period_end + 80 with a plain column and backfilled every row. That one scalar lag was split between two ONS release calendars ~60 days apart, so it was wrong for both — and wrong in the direction that invents a FUTURE date: the June 2026 CPI point claimed 2026-08-20, a month after ONS actually published it (2026-07-22), which hid the freshest real data from any point-in-time filter. release_date cannot serve, being document-level and identical on every row of a series. The date is taken in Europe/London because ONS stamps updateDate at LOCAL MIDNIGHT (00:00Z under GMT, 23:00Z on the previous UTC day under BST), so a naive UTC ::date reads a day early for every release from late March to late October.

Refresh cadence

Obscura refreshes ons_series daily — 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 — 14 columns

The full public column list for ons_series, with the meaning of every field. The same schema is served unauthenticated at https://api.obscura.trade/v1/catalog/ons_series.

ColumnTypeDescription
series_idtext · not nullONS series id (CDID), e.g. `D7BT`. Half of the upsert conflict key together with period_end.
period_enddate · not nullReference-period date for this data point, computed by point_date() from the JSON point's {year, month/quarter} (first day of the reporting month/quarter/year). Other half of the upsert conflict key with series_id.
valuedouble precisionThe observation value for this series at this reference period; an index number (2015=100) or a percent depending on series. Nullable — a point whose value fails to parse is stored as NULL rather than dropped.
datasettext · not nullSource dataset code (e.g. `MM23` for CPI/RPI/CPIH price indices, `LMS` for Labour Market Statistics); also forms part of the source fetch URL.
nametext · not nullHuman-readable label for the series curated in-code, e.g. "CPI 12-month rate (%)"; the only field documenting the value's units/meaning since `value` itself is unitless.
freqtext · not nullReporting frequency label for the series: `monthly` | `quarterly` | `annual`.
available_datedatePUBLIC-availability date — the day ONS published THIS observation, and the only column to filter or join on for point-in-time work. A PLAIN column written by the collector (`tasks::ons`) as `revised_at` read on ONS's own Europe/London clock, not a generated one: the availability of an ONS point is a property of the point, and no table-wide formula over `period_end` can express two release calendars ~60 days apart (the previous `period_end + 80` was look-ahead for the labour-market series and dated the price series into the FUTURE). NULL when the source states no `updateDate` — honestly unknown, never manufactured from the reference period. Never the reference period itself.
revised_attimestamp with time zonePer-point publication/revision timestamp (raw `months[].updateDate`, ISO e.g. `2026-06-16T23:00:00.000Z`): when THIS observation was last published/revised. The true point-in-time / vintage marker per data point, and the column `available_date` is materialized from (converted to Europe/London, since the stamp is local midnight — the example above is 2026-06-17 in London). NULL if the point carries no updateDate.
unittextCanonical unit of measure for `value` (raw `description.unit`, e.g. `%` or blank for an index number). Document-level metadata identical for every point of the series; `value` itself is otherwise unitless.
pre_unittextPrefix unit applied before `value` (raw `description.preUnit`, e.g. a currency prefix `£`). Document-level; usually empty for the curated index/rate series.
source_titletextSource's authoritative series title (raw `description.title`, e.g. `CPI ANNUAL RATE 00: ALL ITEMS 2015=100`). We substitute a hand-curated `name`; this is the canonical label and drift-detects renamed/re-based series. Document-level.
key_notetextShort note describing what `value` measures (raw `description.keyNote`, e.g. `Change over 12 months`). Document-level; NULL when the source omits it.
release_datetimestamp with time zoneLatest actual release timestamp from the ONS release calendar (raw `description.releaseDate`, ISO). DOCUMENT-level, so it is the same value on every row of a series (verified in production: min = max for all six series) — a freshness signal for the series as a whole, and NOT a per-row availability date. The per-point equivalent is `revised_at`.
next_releasedateNext scheduled release date from the ONS release calendar (raw `description.nextRelease`, parsed from a `DD Month YYYY` string, e.g. `16 July 2026`). Document-level; NULL when unparseable or unannounced.

Access ons_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="ons_series",
    symbols=["NVDA", "AAPL"],
    start="2024-01-01",
)

Create a free account Browse all 95 datasets

Frequently asked questions

What is in the ons_series dataset?

UK macroeconomic series from the Office for National Statistics. One published data point for a single curated UK ONS macro series (CPI/CPIH/RPI inflation indices or labour-market rates) for one reference period — e.g. the CPI 12-month rate for May-2026, or the unemployment rate for the March-May 2026 rolling window.

How do I avoid look-ahead bias with ons_series?

Filter on ons_series.available_date, the day the publisher made the row public. For this dataset that date is derived as follows — published: available_date is the point's OWN publication day, materialized by the collector from revised_at (raw months[].updateDate) on ONS's London clock — m20260702_000183 replaced the generated period_end + 80 with a plain column and backfilled every row. That one scalar lag was split between two ONS release calendars ~60 days apart, so it was wrong for both — and wrong in the direction that invents a FUTURE date: the June 2026 CPI point claimed 2026-08-20, a month after ONS actually published it (2026-07-22), which hid the freshest real data from any point-in-time filter. release_date cannot serve, being document-level and identical on every row of a series. The date is taken in Europe/London because ONS stamps updateDate at LOCAL MIDNIGHT (00:00Z under GMT, 23:00Z on the previous UTC day under BST), so a naive UTC ::date reads a day early for every release from late March to late October. A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.

In what formats can I get ons_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="ons_series". The column schema is public at https://api.obscura.trade/v1/catalog/ons_series.

Can I filter ons_series by company or symbol?

Yes. ons_series carries series_id, the column the API's symbols filter resolves against.

How often is ons_series updated?

Obscura refreshes ons_series on a daily 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

BLS SeriesEconomic series from the U.S. Bureau of Labor Statistics, fetched by series ID: employment, unemployment, CPI, PPI, wage… 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. 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').