DEV Community

rmccharles
rmccharles

Posted on

How to Get US and Canadian Tariff Data via API (The Complete Developer Guide)

If you're building anything that touches cross-border trade — a landed cost calculator, a customs compliance tool, a Shopify app that shows import duty rates — you're going to need programmatic access to tariff data. This guide walks through every option, including the free government APIs, their limitations, and what to do when those limitations matter.


The data sources

There are two datasets you need for US/Canada cross-border work:

  • US Harmonized Tariff Schedule (HTS) — maintained by the US International Trade Commission (USITC). 32,295 records covering every importable product category.
  • Canadian Customs Tariff — maintained by the Canada Border Services Agency (CBSA). 22,461 records including MFN rates and preferential rates under CUSMA (the successor to NAFTA).

Both are public. Neither is particularly developer-friendly out of the box.


Option 1: The USITC REST API (free, US only)

The USITC publishes a REST API at hts.usitc.gov. It's real data and it's free. Here's how to use it:

Search by keyword

import requests

resp = requests.get(
    "https://hts.usitc.gov/reststop/api/details/sectionJSON",
    params={"query": "aluminum", "offset": 0, "limit": 20}
)
data = resp.json()

for item in data.get("HTSDetails", []):
    print(item["htsno"], item["description"], item["general"])
Enter fullscreen mode Exit fullscreen mode

Lookup by HTS code

resp = requests.get("https://hts.usitc.gov/reststop/api/details/htsnoJSON/0101.30.00.00")
detail = resp.json()
print(detail["htsno"])       # 0101.30.00.00
print(detail["description"]) # Asses
print(detail["general"])     # 6.8%
print(detail["special"])     # Free (A+,AU,BH,CL...)
Enter fullscreen mode Exit fullscreen mode

Get an entire chapter

resp = requests.get("https://hts.usitc.gov/reststop/api/details/sectionJSON",
    params={"query": "chapter:84", "limit": 500})
Enter fullscreen mode Exit fullscreen mode

This works. The data is accurate. But there are real limitations you'll hit quickly.


Where the USITC API falls short

No Canadian data. The USITC API covers US imports only. If you need to show what a US exporter pays when shipping into Canada — the CUSMA/UST preferential rate, the MFN rate, the over-quota rate on dairy — you're on your own. The CBSA publishes the Canadian tariff as a PDF and an Excel file. There is no CBSA REST API.

No change detection. The USITC updates the HTS schedule — sometimes significantly, especially in a volatile tariff environment. The API doesn't tell you what changed. If you cache HTS data (and you should, for performance), you have no reliable signal for when to invalidate your cache.

Schema instability. The USITC has changed its API response format without notice. If you build a production integration directly on it, plan to babysit it.

No SLA. It's a government endpoint. It goes down. There's no uptime commitment.

Rate limits are unpublished. Hammer it and you'll get throttled, with no clear documentation on what the limits are.

For a side project or internal tool, these tradeoffs are often fine. For a production integration where your customers depend on rate accuracy, they're not.


Option 2: Parsing the CBSA Canadian Tariff yourself

The CBSA publishes the Canadian Customs Tariff as a downloadable Excel file each year. Here's how to parse it in Python:

import requests
import openpyxl
from io import BytesIO

url = "https://www.cbsa-asfc.gc.ca/trade-commerce/tariff-tarif/2026/html/tblmod-1-eng.xlsx"
resp = requests.get(url)
wb = openpyxl.load_workbook(BytesIO(resp.content), data_only=True)
ws = wb.active

records = []
for row in ws.iter_rows(min_row=2, values_only=True):
    tariff_code, description, mfn_rate, ust_rate = row[0], row[1], row[3], row[8]
    if tariff_code:
        records.append({
            "tariff": str(tariff_code),
            "description": description,
            "mfn": mfn_rate,
            "ust": ust_rate,
        })

print(f"Loaded {len(records)} records")
Enter fullscreen mode Exit fullscreen mode

This works — once. The problems start when you need to maintain it:

  • The CBSA changes the Excel column layout between releases without documentation
  • The file URL changes each year
  • Supply management goods (dairy, poultry) have over-quota rates of 200–400% that need special handling
  • There are 6,229 duplicate records where .00 suffix variants exist alongside base codes
  • You're now responsible for storing, serving, and updating this data in your application

Option 3: Use a purpose-built tariff API

If you need both US HTS and Canadian Customs Tariff data with a stable schema and change detection, TradeFacts.io is a REST API built specifically for this. It pulls nightly from USITC and CBSA, normalizes both datasets into consistent JSON, diffs each nightly run to detect changes, and delivers webhook alerts when rates change.

import requests

headers = {"X-API-Key": "your_api_key"}

# US HTS lookup
resp = requests.get("https://tradefacts.io/api/hts/0101.30.00.00", headers=headers)
print(resp.json())

# Canadian tariff lookup
resp = requests.get("https://tradefacts.io/api/ca/0401.10.10", headers=headers)
print(resp.json())

# Search CA dataset
resp = requests.get("https://tradefacts.io/api/ca/search", params={"q": "dairy"}, headers=headers)

# What changed in the last 7 days?
resp = requests.get("https://tradefacts.io/api/changes", params={"days": 7}, headers=headers)
Enter fullscreen mode Exit fullscreen mode

No-auth demo endpoint for evaluation:

curl "https://tradefacts.io/api/demo/search?q=steel&ds=us"
curl "https://tradefacts.io/api/demo/search?q=dairy&ds=ca"
Enter fullscreen mode Exit fullscreen mode

30-day free trial at tradefacts.io — no credit card required.


Choosing the right approach

USITC API CBSA Excel TradeFacts.io
US tariff data
Canadian tariff data ✓ (manual)
Change detection
Webhook alerts ✓ (Pro)
Schema stability
Uptime SLA
Cost Free Free Paid

If you only need US data for a low-stakes integration: use the USITC API directly. If you need Canada, need both, or are building something production-critical: the maintenance cost of rolling your own will exceed the API cost fairly quickly.


Conclusion

Tariff data is genuinely messy infrastructure. The government sources are authoritative but not production-ready. If you're building something that needs to stay current as rates change — especially with the current pace of tariff policy changes — plan for that maintenance cost up front rather than discovering it six months in when a client's landed cost calculations are wrong.

Top comments (0)