Metadata-Version: 2.5
Name: xippios-comtrade
Version: 0.1.0
Summary: Typed client, MCP tools and HTTP routes for UN Comtrade
Project-URL: Homepage, https://github.com/xippios/sdk-comtrade
Project-URL: Upstream, https://comtradeplus.un.org
Project-URL: Changelog, https://github.com/xippios/sdk-comtrade/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/xippios/sdk-comtrade/issues
Author: XIPPIOS GmbH
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: comtrade,httpx,international-trade,mcp,pydantic,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: xippios-core>=0.1.0
Provides-Extra: http
Requires-Dist: xippios-core[http]>=0.1.0; extra == 'http'
Provides-Extra: mcp
Requires-Dist: xippios-core[mcp]>=0.1.0; extra == 'mcp'
Provides-Extra: testing
Requires-Dist: xippios-core[testing]>=0.1.0; extra == 'testing'
Description-Content-Type: text/markdown

# xippios-comtrade

Typed Python client, MCP server and FastAPI routes for
[UN Comtrade](https://comtradeplus.un.org), part of the
[xippios SDK family](https://github.com/xippios). Names and semantics follow the
upstream documentation; every response carries release metadata (source, endpoint,
retrieved_at, published_at, vintage).

## Install

```bash
uv add xippios-comtrade            # client only
uv add "xippios-comtrade[mcp]"     # + MCP server
uv add "xippios-comtrade[http]"    # + FastAPI router
```

Set `COMTRADE_API_KEY` in the environment. Nothing is read from files.

## 30-second example

```python
from xippios.comtrade import get_counts, get_data

# Switzerland (reporter 757) exports to the World (partner 0) in 2023, all products.
query = {
    "reporter_code": "757",
    "partner_code": "0",
    "period": "2023",
    "cmd_code": "TOTAL",
    "flow_code": "X",
}
get_counts("C", "A", "HS", **query).data  # 32: every customs procedure and mode of transport
result = get_data("C", "A", "HS", **query)
total = next(r for r in result.data if (r.customs_code, r.mot_code) == ("C00", 0))
total.primary_value  # Decimal('419922543423.509'); JSON: a number
result.meta.endpoint, result.meta.request  # 'data/v1/get', the arguments above
```

Parameters are upstream's (`typeCode`, `reporterCode`, ...) in snake_case and take
upstream's codes, several of them comma-separated as upstream takes them (`"757,842"`).
Reporter and partner codes are UN Comtrade's, not ISO numeric: Switzerland is 757.
Every argument is checked before the call: coded ones (`type_code`, `cl_code`,
`flow_code`, `customs_code`, `mot_code`, `aggregate_by`, `breakdown_mode`) against
the lists packaged from upstream's reference files and spec (`xippios.comtrade.enums`),
`reporter_code` against the packaged copy of upstream's `Reporters.json`
(`reference/Reporters.csv`) and `partner_code` and `partner2_code` against that of
`partnerAreas.json`, the others by shape; a mistake raises
`InvalidRequestError` naming the closest valid value (`756` names `757`, Switzerland).

```python
from xippios.comtrade import list_commodities, list_partners, list_reporters

list_reporters().data  # 255 reporters, packaged with this version: no call
list_reporters(search="switz").data  # code, ISO alpha-3 or name, any case: 757
list_partners().data  # 310 partner areas, 0 World among them
list_commodities("HS", search="coffee").data  # from 8,262 HS codes, fetched keyless once
```

Four task-shaped slices answer the common questions in one call each (the series in one
call per 12 periods), at the total of every breakdown, one row per reporter × partner ×
product code × period. Areas are UN Comtrade's codes or ISO alpha-3 codes; `YYYY` periods
ask for annual data, `YYYYMM` for monthly:

```python
from xippios.comtrade import (
    get_bilateral_flows,
    get_commodity_series,
    get_top_partners,
    get_world_trade,
)

get_bilateral_flows("CHE", "USA", "TOTAL,27", "2022,2023", "X").data  # 4 rows
years = ",".join(str(year) for year in range(2010, 2024))
get_commodity_series("757", "27", "X", years).data  # 14 years, two calls, by period
get_world_trade("27", "2023", "M").data[0].reporter_desc  # 'China': largest first
[r.partner_desc for r in get_top_partners("CHE", "TOTAL", "2023", "X", 3).data]
# ['USA', 'Germany', 'China']: World, the total, is dropped
```

Release dates are per dataset, so they sit on the rows, not in `meta.published_at`:

```python
from xippios.comtrade import get_data_availability, get_live_update, get_metadata

[dataset] = get_data_availability("C", "A", "HS", reporter_code="757", period="2023").data
dataset.first_released, dataset.last_released  # first publication and latest revision
notes = get_metadata("C", "A", "HS", reporter_code="757", period="2023").data[0].notes
[note.publication_date for note in notes]  # "New Data", then each "Revised Data"
get_live_update().data  # the 50 most recent releases, newest first
```

`get_data` counts first with upstream's `countOnly`: a count of zero returns an empty
result without a second call, and a count above 100,000 raises `QueryTooLarge` with the
count before anything is fetched, because upstream would cut the answer at 100,000 rows
without saying so. A free key allows 1 call per second and 500 calls per day, and
`countOnly` calls count; the client spaces calls 1.5 s apart (upstream's gateway refuses
some calls spaced exactly 1 s apart), the daily quota is yours to track.

## MCP

```json
{
  "mcpServers": {
    "comtrade": {
      "command": "uvx",
      "args": ["--from", "xippios-comtrade[mcp]", "xippios-comtrade-mcp"],
      "env": { "COMTRADE_API_KEY": "..." }
    }
  }
}
```

Tools are read-only and named `comtrade_<verb>_<noun>`. Results are capped at
200 rows or 64 KB with a note on how to narrow the query. Unfiltered,
`comtrade_list_reporters`, `comtrade_list_partners` and `comtrade_list_commodities` show
the first rows of their table only (200 of 255 reporters, in upstream's alphabetical
order), so they take `search`: `{"search": "switz"}` returns 757 Switzerland. `--http`
serves streamable HTTP instead of stdio.

## Status

| Upstream operation | Task function | MCP tool | Status |
| --- | --- | --- | --- |
| `data/v1/get/{typeCode}/{freqCode}/{clCode}` with `countOnly=true` | `get_counts(type_code, freq_code, cl_code, *, reporter_code=, partner_code=, ...)` | `comtrade_get_counts` | alpha |
| `data/v1/get/{typeCode}/{freqCode}/{clCode}` | `get_data(type_code, freq_code, cl_code, *, reporter_code=, partner_code=, ...)` | `comtrade_get_data` | alpha |
| `data/v1/getDa/{typeCode}/{freqCode}/{clCode}` | `get_data_availability(type_code, freq_code, cl_code, *, reporter_code=, period=, published_date_from=, published_date_to=)` | `comtrade_get_data_availability` | alpha |
| `data/v1/getMetadata/{typeCode}/{freqCode}/{clCode}` | `get_metadata(type_code, freq_code, cl_code, *, reporter_code=, period=)` | `comtrade_get_metadata` | alpha |
| `data/v1/getLiveUpdate` | `get_live_update()` | `comtrade_get_live_update` | alpha |
| `data/v1/get/C/{A,M}/{clCode}`, total row, one reporter and partner | `get_bilateral_flows(reporter, partner, commodities, periods, flow, *, cl_code="HS")` | `comtrade_get_bilateral_flows` | alpha |
| `data/v1/get/C/{A,M}/{clCode}`, total row, partner World, 12 periods per call, at most 120 | `get_commodity_series(reporter, commodity, flow, periods, *, cl_code="HS")` | `comtrade_get_commodity_series` | alpha |
| `data/v1/get/C/{A,M}/{clCode}`, total row, every reporter, partner World | `get_world_trade(commodity, period, flow, *, cl_code="HS")` | `comtrade_get_world_trade` | alpha |
| `data/v1/get/C/{A,M}/{clCode}`, total row, every partner, ranked | `get_top_partners(reporter, commodity, period, flow, n, *, cl_code="HS")` | `comtrade_get_top_partners` | alpha |
| `files/v1/app/reference/Reporters.json`, packaged | `list_reporters(search=None)` | `comtrade_list_reporters` | alpha |
| `files/v1/app/reference/partnerAreas.json`, packaged | `list_partners(search=None)` | `comtrade_list_partners` | alpha |
| `files/v1/app/reference/{clCode}.json`, keyless | `list_commodities(cl_code, search=None)` | `comtrade_list_commodities` | alpha |

All twelve are covered in 0.1.0, the first release. Alpha: the public surface may change
in a minor version before 1.0; `CHANGELOG.md` lists every change.

## Upstream terms and attribution

Data comes from UN Comtrade (https://comtradeplus.un.org) under its own terms of use;
this package adds no rights to it. Documentation snapshot: `docs/upstream/`.

## Licence

Apache-2.0. See [LICENSE](LICENSE).
