Comtrade Flows
Annual bilateral goods-trade totals between countries, from UN Comtrade.
comtrade_flows — the dataset name to pass to the Obscura API.
What one row means
One reporter country's annual total trade value (all commodities, HS "TOTAL") with the World, for one flow direction (export or import) in one calendar year, pulled from UN Comtrade's keyless preview API (comtradeapi.un.org/public/v1/preview/C/A/HS?...&cmdCode=TOTAL&partnerCode=0).
One row per (reporter_code, partner_code, period, flow_code, cmd_code); effectively (reporter_code, period, flow_code) today since partner_code is always 0/World and cmd_code is always TOTAL, the only slice the collector requests..
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 comtrade_flows: published: available_date = period_end + 450 days, a conservative year-end-plus-lag estimate for annual UN Comtrade trade data (real-world publication lag is variable).
Refresh cadence
Obscura refreshes comtrade_flows 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 — 16 columns
The full public column list for comtrade_flows, with the meaning of every field. The same
schema is served unauthenticated at https://api.obscura.trade/v1/catalog/comtrade_flows.
| Column | Type | Description |
|---|---|---|
| reporter_code | integer · not null | UN M49 numeric code of the reporting country (e.g. 842 = USA, 156 = China). Part of the composite PK; from API field `reporterCode`; the reporter list is configured in ComtradeArgs.reporters. |
| partner_code | integer · not null | UN M49 code of the trade partner; the collector always queries partnerCode=0 (World), so this is always 0 in stored rows today. Part of the composite PK; from API field `partnerCode`, defaulting to 0 if absent. |
| period | integer · not null | Calendar year the annual trade total describes (e.g. 2025), from API field `period`. This is the dataset's true reference period. Part of the composite PK; the current year is always re-queried since its total still accrues. |
| flow_code | text · not null | Trade direction: "X" = export, "M" = import, from API field `flowCode`. Part of the composite PK; ComtradeArgs.flows defaults to ["X","M"]. |
| cmd_code | text · not null | HS commodity code; the collector only ever requests/stores "TOTAL" (all goods aggregated) because the keyless preview API caps rows per query. Falls back to "TOTAL" if omitted. Part of the composite PK. |
| reporter_desc | text | Human-readable reporter country name from API field `reporterDesc` (e.g. "USA"). Nullable, purely descriptive, not part of the key. |
| partner_desc | text | Human-readable partner name from API field `partnerDesc`; since partner_code is always 0/World, typically null or "World" — Comtrade often omits partnerDesc for the World aggregate. |
| primary_value | double precision | Total trade value in US dollars for this reporter/period/flow, from API field `primaryValue`; nominal USD, not inflation- or seasonally-adjusted. Nullable — rows missing the field are still stored (only missing reporter_code/period drops a row). |
| net_weight_kg | double precision | Total net weight of traded goods in kilograms, from API field `netWgt`. Frequently 0 or null at the TOTAL/World aggregate level this collector requests — Comtrade doesn't reliably populate physical-quantity totals at that aggregation. |
| cif_value | double precision | CIF (cost-insurance-freight) valuation basis in nominal USD, from API field `cifvalue`. Populated for import flows; on imports `primaryValue` carries this figure, so keeping it alongside `fob_value` yields the CIF−FOB freight+insurance margin. Null at this export/aggregate slice. |
| fob_value | double precision | FOB (free-on-board) valuation basis in nominal USD, from API field `fobvalue`. Populated for export flows and (on imports) alongside `cif_value` to expose the freight+insurance margin; not derivable from `primaryValue` alone. |
| gross_weight_kg | double precision | Gross weight of traded goods in kilograms, from API field `grossWgt`; a distinct physical measure alongside `net_weight_kg`. 0 at the TOTAL/World aggregate this collector requests, populated at commodity detail. |
| reporter_iso | text | ISO alpha country code of the reporter, from API field `reporterISO`; a canonical country identifier complementing the M49 numeric `reporter_code`. Null on every row of this preview/aggregate slice today (the full API populates it). |
| partner_iso | text | ISO alpha country code of the trade partner, from API field `partnerISO`; a canonical country identifier complementing the M49 numeric `partner_code`. Null on every row of this preview/aggregate slice today (the full API populates it). |
| period_end | date | Trade year-end date: Dec 31 of `period` (the year the annual total describes). STORED generated column = make_date(period, 12, 31); the true reference period-end for this row, not a publication date. |
| available_date | date | Conformed public-availability date. STORED generated column = period_end + 450 days, a conservative year-end-plus-lag estimate for when this annual total is realistically public; the point-in-time column to filter/join on. |
Access comtrade_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="comtrade_flows",
start="2024-01-01",
)
Create a free account Browse all 95 datasets
Frequently asked questions
What is in the comtrade_flows dataset?
Annual bilateral goods-trade totals between countries, from UN Comtrade. One reporter country's annual total trade value (all commodities, HS "TOTAL") with the World, for one flow direction (export or import) in one calendar year, pulled from UN Comtrade's keyless preview API (comtradeapi.un.org/public/v1/preview/C/A/HS?...&cmdCode=TOTAL&partnerCode=0).
How do I avoid look-ahead bias with comtrade_flows?
Filter on comtrade_flows.available_date, the day the publisher made the row public. For this dataset that date is derived as follows — published: available_date = period_end + 450 days, a conservative year-end-plus-lag estimate for annual UN Comtrade trade data (real-world publication lag is variable). A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.
In what formats can I get comtrade_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="comtrade_flows". The column schema is public at https://api.obscura.trade/v1/catalog/comtrade_flows.
How often is comtrade_flows updated?
Obscura refreshes comtrade_flows 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.