UK Payment Flows
Monthly totals of real UK inter-organisation payments - Bacs Direct Debit, Bacs Direct Credit and Faster Payments - by paying and receiving industry and UK region, with both the value transferred and the number of transactions.
ons_payment_flows - the dataset name to pass to the Obscura API.
What one row means
One month's total Bacs/Faster Payments value and transaction count from one payer cell to one payee cell, where a cell is an industry and/or a UK region.
One row per (resolution, payer_sic, payer_region, payee_sic, payee_region, occurred_on).
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_payment_flows: published: available_date is a plain collector-written column carrying the release day of the FIRST ONS edition that contained this (resolution, month), taken from that edition's own metadata (`releaseDate`, converted to the Europe/London calendar day) and corroborated by the workbook Cover sheet's 'Released:' cell. It is PER EDITION and deliberately not a blanket floor: measured 2026-08-25, the SIC2/SIC5 editions release on 2026-05-21, the three by-region editions on 2026-06-18 and region-to-region on 2026-01-30, so one floor across the family would publish the by-region member about nineteen weeks early. occurred_on is the first day of the month the payments were made in, which is weeks earlier than available_date and must never be used as the point-in-time basis.
Refresh cadence
Obscura refreshes ons_payment_flows monthly - 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 - 12 columns
The full public column list for ons_payment_flows, with the meaning of every field. The same
schema is served unauthenticated at https://api.obscura.trade/v1/catalog/ons_payment_flows.
| Column | Type | Description |
|---|---|---|
| resolution | text · not null | WHICH MARGIN OF THE PAYMENT CUBE this row is, and the first component of the key: `sic2`, `sic5`, `industry_region` or `region_region`. Every resolution restates the SAME underlying payments at a different breakdown, so **summing across this column double-counts the economy** - always filter to exactly one value before aggregating. It is redundant by construction (derivable from the SIC code width and whether the region columns are sentinels) and is in the key anyway, so the rule is a column a consumer can see rather than folklore. |
| payer_sic | text · not null | Industry of the PAYING organisation as the SIC code string exactly as ONS publishes it - 2-digit (`"46"`) at `sic2`, 5-digit (`"46110"`) at `sic5`. `"ALL"` is the explicit margin sentinel, used on resolutions that do not break the payer out by industry. Never NULL: a sentinel says "summed over this axis", a NULL would say "unknown", and they are different facts. |
| payer_region | text · not null | ITL1 region of the PAYING organisation (one of the twelve ONS ITL1 codes), or `"ALL"` where the resolution does not break the payer out by region. |
| payee_sic | text · not null | Industry of the RECEIVING organisation, on exactly the same code system and sentinel convention as `payer_sic`. |
| payee_region | text · not null | ITL1 region of the RECEIVING organisation, same convention as `payer_region`. |
| occurred_on | date · not null | THE MONTH THE PAYMENTS WERE MADE IN, as that month's first calendar day. This is the event date and is weeks earlier than `available_date` - ONS publishes a month's flows well after the month ends. **Never filter or join point-in-time on this**; that is `available_date`. |
| value_gbp | bigint | Total value in POUNDS STERLING of payments from the payer cell to the payee cell during `occurred_on`'s month. NULL when the cell is suppressed or genuinely absent - read `cell_status` to tell which, rather than treating a NULL as a zero. A suppressed cell is not an empty one. |
| transaction_count | bigint | Number of individual Bacs Direct Debit, Bacs Direct Credit and Faster Payments transactions behind `value_gbp`. NULL under the same rules. |
| cell_status | text · not null | WHY the measures are or are not populated, so a NULL is never ambiguous: `published` (both measures present), `suppressed` (ONS withheld the cell for disclosure control - a real flow exists and its size is withheld), or `absent` (no payments observed between these cells that month). Collapsing suppressed and absent into one NULL would make "we may not tell you" and "it did not happen" indistinguishable, which for a disclosure-controlled series is the difference between a gap and a zero. |
| regime | text · not null | The METHODOLOGY ERA these numbers were produced under - see [`REGIMES`]. ONS rebuilt the series between the experimental 2016-2023 publication and the current maintained editions, so a difference taken across 2024-01-01 measures the rebuild rather than the economy. |
| available_date | date · not null | PUBLIC-AVAILABILITY DATE - the day this `(resolution, month)` FIRST became readable, from the ONS edition's own `releaseDate` on the Europe/London calendar. **The point-in-time column: filter and join on this, never on `occurred_on`.** Per edition, not a family-wide floor - the four editions release on genuinely different days and a blanket value would publish the by-region member months before it existed. |
| source_release_date | date | Release day of the file this row's CURRENT values were last read from, when that is a later edition than the one that first published it. ONS revises: a restated cell keeps its original `available_date` (when the figure first became knowable) and updates this, so the two together say "known since X, last restated by the Y release". |
Access ons_payment_flows
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_payment_flows",
start="2024-01-01",
)
Create a free account Browse all 122 datasets
Frequently asked questions
What is in the ons_payment_flows dataset?
Monthly totals of real UK inter-organisation payments - Bacs Direct Debit, Bacs Direct Credit and Faster Payments - by paying and receiving industry and UK region, with both the value transferred and the number of transactions. One month's total Bacs/Faster Payments value and transaction count from one payer cell to one payee cell, where a cell is an industry and/or a UK region.
How do I avoid look-ahead bias with ons_payment_flows?
Filter on ons_payment_flows.available_date, the day the publisher made the row public. For this dataset that date is derived as follows - published: available_date is a plain collector-written column carrying the release day of the FIRST ONS edition that contained this (resolution, month), taken from that edition's own metadata (`releaseDate`, converted to the Europe/London calendar day) and corroborated by the workbook Cover sheet's 'Released:' cell. It is PER EDITION and deliberately not a blanket floor: measured 2026-08-25, the SIC2/SIC5 editions release on 2026-05-21, the three by-region editions on 2026-06-18 and region-to-region on 2026-01-30, so one floor across the family would publish the by-region member about nineteen weeks early. occurred_on is the first day of the month the payments were made in, which is weeks earlier than available_date and must never be used as the point-in-time basis. 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_payment_flows?
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_payment_flows". The column schema is public at https://api.obscura.trade/v1/catalog/ons_payment_flows.
How often is ons_payment_flows updated?
Obscura refreshes ons_payment_flows on a monthly 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.