tarifftrust: US tariff API client for Python
The official Python client for the TariffTrust API. Send an HTS code, a country of origin and a value. Get every US import duty and fee that applies, from the tariff schedule and Sections 301, 232, 201 and 338 to the entry fees, each with its official, dated source.
pip install tarifftrust
Requires Python 3.10 or later. Fully typed, sync and async.
Quick start
Create a key in your workspace; your plan must include the API (plans). Keep it on your server.
from tarifftrust import TariffTrust
client = TariffTrust(api_key="tarifftrust_...") # or set TARIFFTRUST_API_KEY
result = client.estimate(hts="6109.10.00.12", origin="CN", customs_value="10000")
print(result.status, result.estimated_duty)
for component in result.components:
print(component.label, component.amount, component.source.url)
Read status first, then the amounts: decimal strings in US dollars, ready for Decimal.
Unknown is never zero. When estimated_duty is None, the duty could not be established: only
known_duty_subtotal is. See the guide.
A whole declaration
MPF and HMF are charged once on a declaration: adding up single-product estimates gives a wrong total.
entry = client.estimate_entry(
transport_mode="ocean",
lines=[
{"reference": "PO-1042-1", "hts": "6109.10.00.12", "origin": "CN", "customs_value": "1000"},
{"reference": "PO-1042-2", "hts": "6109.10.00.12", "origin": "VN", "customs_value": "2500"},
],
)
print(entry.estimated_duty, entry.total_fees, entry.estimated_landed_cost)
Several products, a history, a saved estimate
batch = client.estimate_batch(
items=[
{"hts": "6109.10.00.12", "origin": "CN", "customs_value": "10000"},
{"hts": "6109.10.00.12", "origin": "VN", "customs_value": "10000"},
]
)
for item in batch.results:
print(item.index, item.result.estimated_duty if hasattr(item, "result") else item.error.code)
history = client.estimate_history(
hts="6109.10.00.12", origin="CN", customs_value="10000", from_="2026-10-01"
)
for point in history.points:
print(point.as_of, point.estimated_duty)
saved = client.save_estimate(
estimate={"hts": "6109.10.00.12", "origin": "CN", "customs_value": "10000"}
)
print(saved.id)
from is a reserved word in Python: it is from_ here, as an argument and on an answer.
The catalog
found = client.search_hts(q="cotton t-shirt", limit=5)
line = client.get_hts("6109.10.00.12")
print(line.description, line.general)
Async
AsyncTariffTrust has the same methods, awaited.
import asyncio
from tarifftrust import AsyncTariffTrust
async def main():
async with AsyncTariffTrust() as client:
return await asyncio.gather(
client.estimate(hts="6109.10.00.12", origin="CN", customs_value="10000"),
client.estimate(hts="6109.10.00.12", origin="VN", customs_value="10000"),
)
Errors
Each refusal of the API is its own exception, under APIError.
from tarifftrust import APIError, InvalidInputError, RateLimitError
try:
result = client.estimate(hts="6109.10.00.12", origin="??", customs_value="10000")
except InvalidInputError as error:
print(error.fields) # each field refused
except APIError as error:
print(error.status, error.code, error.message, error.request_id)
| Exception | Status | When |
|---|---|---|
AuthenticationError |
401 | No valid API key |
AccessRequiredError |
402 | No active subscription, or the quota is used up (quota_exceeded) |
PermissionDeniedError |
403 | The key lacks the permission, or the plan the API |
NotFoundError |
404 | Nothing of that name |
ConflictError |
409 | An idempotency key sent again with another request |
PayloadTooLargeError |
413 | Send fewer products or lines |
InvalidInputError |
400, 422 | fields names each field refused; a 400 is a body that is not valid JSON |
RateLimitError |
429 | Too many requests |
ServiceUnavailableError |
502, 503, 504 | Unavailable for now |
APIConnectionError, APITimeoutError |
The API could not be reached |
All of them inherit from TariffTrustError.
Retries and idempotency
A call is tried again, twice by default, after a connection failure, a timeout or a gateway error, or
when the API gives a Retry-After of 60 seconds or less. A refusal without one is raised at once:
trying again would not help.
Every estimate carries an idempotency key, the same on each attempt, so a retry counts once in your quota. To make a retry of your own safe, across restarts for instance, give your own key:
client.estimate(hts="6109.10.00.12", origin="CN", customs_value="10000", idempotency_key="po-1042")
Within 7 days, the same key with the same request gives the first response, counted once; with
another request, a ConflictError.
Settings
client = TariffTrust(
api_key="tarifftrust_...", # or TARIFFTRUST_API_KEY
timeout=60.0, # seconds, at each step of an attempt: connecting, sending, each read
max_retries=2,
max_retry_wait=60.0, # the longest Retry-After waited for, in seconds
http_client=None, # your own httpx.Client (a proxy, your certificates): yours to close
)
The answers
An answer is an object with the fields of the reference
as attributes, typed for your editor. A field the answer leaves out reads as its default or None. A
field newer than your version of the package reads as an attribute too. to_dict() gives the object
as the API sent it, and the types are in tarifftrust.types.
Operations
| Method | Route | What it does |
|---|---|---|
client.list_agreements() |
GET /v1/agreements |
List the trade agreements |
client.estimate() |
POST /v1/estimates |
Estimate one product |
client.estimate_batch() |
POST /v1/estimates/batch |
Estimate up to 200 products |
client.estimate_entry() |
POST /v1/estimates/entry |
Estimate a whole import declaration |
client.estimate_history() |
POST /v1/estimates/history |
Estimate one product at every change of the tariff |
client.list_facts() |
GET /v1/facts |
List the questions an estimate may ask |
client.search_hts() |
GET /v1/hts |
Search the US tariff schedule |
client.get_hts() |
GET /v1/hts/{code} |
Read one line of the US tariff schedule |
client.list_saved_estimates() |
GET /v1/saved-estimates |
List the saved estimates |
client.save_estimate() |
POST /v1/saved-estimates |
Calculate and save an estimate |
client.get_saved_estimate() |
GET /v1/saved-estimates/{id} |
Read a saved estimate |
client.get_status() |
GET /v1/status |
Read the status and the coverage of the data |
Links
API reference · Integration guides · Examples · Changelog · Data status
TariffTrust provides informational estimates from official US sources. It is not a customs broker and does not issue binding classifications or customs determinations.
Metadata
Release files for tarifftrust 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| tarifftrust-0.1.0.tar.gz | 68.6 kB | With provenance | Details for tarifftrust-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| tarifftrust-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for tarifftrust-0.1.0-py3-none-any.whl |
Total release size: 98.4 kB
Release files / tarifftrust-0.1.0.tar.gz
| Download URL | tarifftrust-0.1.0.tar.gz |
|---|---|
| Size | 68.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c6cd5bf95e7b11da9939889e699f806b8edcdb7f34f2d1405452ed7532980467
|
|
BLAKE2b-256 checksum How to use checksums |
43cc7bffe5b23538aebefd4da1dba63a34d039320b2814efbf04aea6a4eea85f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.
Transparency logRelease files / tarifftrust-0.1.0-py3-none-any.whl
| Download URL | tarifftrust-0.1.0-py3-none-any.whl |
|---|---|
| Size | 29.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
21171901d45bb5ec111a86705d818a1a9db9c082c24af5810ae0c6f8a056e4a2
|
|
BLAKE2b-256 checksum How to use checksums |
b734491bb1bcad2763230732ab93621a65ee2ddaaf14c23f0e2c1ae9f3c6fc22
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.
Transparency logSecurity for tarifftrust 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for tarifftrust-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.