# UN Comtrade Scraper: Global Trade Flows by Commodity (`arman-bd/un-comtrade-scraper`) Actor

Scrape UN Comtrade for international trade statistics: imports and exports by reporter, partner, HS commodity code, value and quantity. No login, no proxy, no browser.

- **URL**: https://apify.com/arman-bd/un-comtrade-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Business, Developer tools, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.37 / 1,000 flow scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## UN Comtrade Scraper: Global Trade Flows by Commodity

![UN Comtrade Scraper: Annual imports and exports by reporter, partner and HS commodity, trade value, net weight and quantity, aggregated so the totals actually add up](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/un-comtrade-scraper.jpg)

**UN Comtrade Scraper** pulls annual international trade statistics from **UN Comtrade**, who imports and exports what, from whom, in which HS commodity category, with trade value, net weight and quantity.

Comtrade is the authoritative record of merchandise trade, compiled from what governments themselves report. This Actor reads its public preview endpoint directly: **no browser, no proxies, no login, no subscription key.**

> **On authentication.** UN Comtrade's full API (`/data/v1/get/.`) requires a paid subscription key. This Actor is built entirely against the **free public preview tier** (`/public/v1/preview/C/A/HS`), which is open, verified by direct probe with no credentials. The preview tier returns at most **500 rows per request**; see *Limits and behaviour* for how that is worked around and when it bites.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/un-comtrade-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/un-comtrade-scraper.md
```

### What you get

| Output field | Meaning |
|---|---|
| `reporterCode`, `reporterName` | The country doing the reporting (UN M49 code plus resolved name) |
| `partnerCode`, `partnerName` | The trading partner; code `0` is Comtrade's "World" aggregate |
| `period` | Calendar year |
| `flowCode` | `M` = imports, `X` = exports |
| `cmdCode`, `cmdDesc` | HS commodity code plus its resolved description |
| `tradeValue` | Reported trade value in USD |
| `netWeight` | Net weight in kg, where reported |
| `qty`, `qtyUnit` | Quantity and its unit (`u` items, `kg`, `l`, `m²`, …) |
| `scrapedAt` | Run timestamp |

A `RUN_SUMMARY` record in the key-value store holds per-run counts, the filters used, any request that failed, and how many requests hit the 500-row ceiling.

### Common use cases

- **Map supply-chain dependencies.** Pull one HS code across every reporter and see which countries actually supply it.
- **Track tariff and sanction impact.** Compare the same reporter/partner/commodity across years either side of a policy change.
- **Size an export market.** Total imports of your product category by country, straight from customs data.
- **Trade-balance analysis.** Request `flow: "M,X"` and both directions arrive in one pass.
- **Commodity concentration risk.** `AG2` expands to all 97 two-digit headings, giving a full profile of a country's trade in one request.

### Quick start

One country's total trade with the world:

```json
{
 "reporters": ["842"],
 "commodityCodes": ["TOTAL"],
 "years": ["2022"],
 "flow": "M,X"
}
```

Full commodity profile, all 97 HS chapters:

```json
{
 "reporters": ["842"],
 "commodityCodes": ["AG2"],
 "years": ["2021", "2022"],
 "flow": "M",
 "maxResults": 500
}
```

Bilateral flows for a specific product:

```json
{
 "reporters": ["276", "842"],
 "partners": ["156", "826"],
 "commodityCodes": ["8703", "27"],
 "years": ["2021", "2022"],
 "flow": "M,X",
 "maxResults": 200
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `reporters` | array | - | **Required.** UN M49 numeric country codes. A label like `842 - USA` is accepted; the code is extracted. |
| `partners` | array | `["0"]` | Partner country codes. Empty means partner `0`, the **World** aggregate, which is what you want for a country's total trade. |
| `commodityCodes` | array | `["TOTAL"]` | `TOTAL`, `AG2`/`AG4`/`AG6` for every 2-/4-/6-digit heading, or specific codes like `8703`. |
| `years` | array | *(current year − 2)* | Calendar years. One request per year, the preview tier accepts only one period per call. |
| `flow` | string | `M` | `M` imports, `X` exports, `M,X` both. |
| `maxResults` | integer | `5000` | Total cap on saved flows. `0` = no limit. |

#### Country codes

Comtrade uses UN M49 numeric codes, not ISO letters.

| Country | Code | | Country | Code |
|---|---|---|---|---|
| World (aggregate) | `0` | | Germany | `276` |
| USA | `842` | | United Kingdom | `826` |
| China | `156` | | Japan | `392` |
| India | `699` | | France | `251` |

The full list is the public `Reporters` and `partnerAreas` reference tables, which this Actor also uses to resolve names.

### Output example

```json
{
 "reporterCode": 276,
 "reporterName": "Germany",
 "partnerCode": 156,
 "partnerName": "China",
 "period": "2021",
 "flowCode": "M",
 "cmdCode": "8703",
 "cmdDesc": "Motor cars and other motor vehicles; principally designed for the transport of persons…",
 "tradeValue": 1152703769.201,
 "netWeight": 55997483.106,
 "qty": 72357.005,
 "qtyUnit": "u",
 "scrapedAt": "2026-08-06T11:56:24.766Z"
}
```

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~un-comtrade-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "reporters": ["842"],
 "commodityCodes": ["AG2"],
 "years": ["2022"],
 "flow": "M"
 }'
```

### JavaScript example

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/un-comtrade-scraper').call({
 reporters: ['842', '276'],
 partners: ['156'],
 commodityCodes: ['TOTAL'],
 years: ['2021', '2022'],
 flow: 'M,X',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const f of items) console.log(`${f.period} ${f.reporterName} ${f.flowCode} ${f.partnerName}: $${f.tradeValue}`);
```

### Limits and behaviour

- **Rows are the aggregate flow, not a breakdown, and that matters.** Left alone, the endpoint returns one row per *customs procedure × mode of transport × second partner*. For German car imports from China in 2021 that is 131 rows summing to $9.2 bn against a true total of $1.15 bn, an 8× overcount for anyone adding up `tradeValue`. This Actor always requests the "all procedures, all modes, all origins" aggregate, so **one row is one flow and the values add up correctly**.
- **Names are resolved, not returned.** The preview tier answers `null` for `reporterDesc`, `partnerDesc`, `cmdDesc` and `qtyUnitAbbr` on every single row. This Actor loads the public `Reporters`, `partnerAreas` and `HS` reference tables once per run and fills those names in. If a reference table is unavailable the run continues with `null` names rather than failing.
- **One year per request.** The preview tier rejects multiple periods outright (`"Maximum number of periods for preview is 1"`), so work is chunked by year, and by groups of 20 commodity codes, which keeps most requests under the row ceiling.
- **500 rows per request is a hard ceiling.** A request that comes back with exactly 500 rows was truncated; the Actor logs a warning naming the request and counts it in `RUN_SUMMARY.requestsTruncated`. Split by fewer commodity codes or fewer partners to get the rest.
- **It is slow, by design of the source.** The endpoint spends 3-6 s in its query planner before answering. Requests allow 120 s, a 1.5 s pause sits between them, and 429/5xx are retried with exponential backoff, the endpoint throttles with "try again in 2 seconds" under even light load.
- **Failures never abort the run.** A rejected request is recorded in `RUN_SUMMARY.failures` and the rest continue. The Actor only throws if *every* request fails, which is what happens if the reporter code itself is invalid, and the API's own explanation is passed through in the error.
- **Public data only.** No authentication, no personal data, no access-control bypass.

### FAQ

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Do I need a Comtrade subscription key?** Not for this Actor. The full `/data/v1/get/` API does require one; the preview tier this Actor uses does not. The trade-off is the 500-row-per-request ceiling.

**Why is the data two years old?** Comtrade publishes annual statistics once member states report and the UN validates them, which typically lags 18-24 months. If you leave `years` empty the Actor defaults to *current year − 2* for exactly that reason.

**Why is `qtyUnit` null?** Because the reporter did not record a quantity for that flow, the underlying `qtyUnitCode` is `-1`. `tradeValue` is still present and valid.

**Why did I get 500 rows exactly?** That is the preview ceiling, not the real answer. Check `RUN_SUMMARY.requestsTruncated` and narrow the query.

**What does partner code 0 mean?** "World", the sum of all partners. It is the default, and it is what you want for a country's total imports or exports.

**What happens if Comtrade is unavailable?** The request is retried with backoff, then recorded in `RUN_SUMMARY.failures`; the run continues with the remaining requests.

**Can I schedule it?** Yes, it is designed for scheduled runs, though the source updates annually rather than daily.

**Can I integrate it with something else?** Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.

# Actor input Schema

## `reporters` (type: `array`):

UN M49 numeric codes for the countries doing the reporting, e.g. 842 (USA), 156 (China), 276 (Germany). A label like '842 - USA' is accepted; the code is extracted automatically.

## `partners` (type: `array`):

UN M49 numeric codes for the trading partners. Leave empty to use partner 0, Comtrade's 'World' aggregate. that is what you want for a country's total trade.

## `commodityCodes` (type: `array`):

HS codes to fetch. 'TOTAL' is all commodities combined; 'AG2', 'AG4' and 'AG6' expand to every 2-, 4- or 6-digit heading; a specific code like '8703' returns just that heading.

## `years` (type: `array`):

Calendar years to fetch. The preview tier accepts one year per request, so each extra year is another request. Comtrade lags roughly two years behind, so the current year is usually empty.

## `flow` (type: `string`):

M = imports, X = exports. 'M,X' returns both in one pass.

## `maxResults` (type: `integer`):

Cap the total number of trade flows saved. Set 0 for no limit.

## Actor input object example

```json
{
  "reporters": [
    "842",
    "156"
  ],
  "partners": [
    "156",
    "276"
  ],
  "commodityCodes": [
    "AG2",
    "8703"
  ],
  "years": [
    "2021",
    "2022"
  ],
  "flow": "M,X",
  "maxResults": 5000
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "reporters": [
        "842"
    ],
    "commodityCodes": [
        "TOTAL"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/un-comtrade-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "reporters": ["842"],
    "commodityCodes": ["TOTAL"],
}

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/un-comtrade-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "reporters": [
    "842"
  ],
  "commodityCodes": [
    "TOTAL"
  ]
}' |
apify call arman-bd/un-comtrade-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/un-comtrade-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7ckO633grkdRvjjgd/builds/ZOFKmoRwyJO8LUaJF/openapi.json
