Changelog

V.V.1.0.7
New Feature:

Headings that state their rate in words now return a pointer

Some headings never state a number. They tell you to look somewhere else:

* `8206.00.00.00` (tool sets) — "the rate of duty applicable to that article in the set
subject to the highest rate of duty"
* `6103.22.00` (ensembles) — "the rate applicable to each garment in the ensemble if
separately entered"
* `9110.11.00.00` (watch movements) — "the rate applicable to the complete, assembled
movement"
* `9005.90.40.00` (optical parts) — "the rate applicable to the article of which it is a
part or accessory"

These used to come back as `rate_type: "unparsed"`, with the sentence in `raw_rate` and
nothing a program could act on. They are now `rate_type: "reference"` and carry
`base_tariff.refers_to`, alongside a `BASE_RATE_REFERENCE` warning,
`summary.base_rate_known: false` and a confidence of 0.5.

Eight pointer kinds are now recognised:

* `set_highest` — the highest-rated article in the set
* `ensemble_component` — each garment, rated as if entered separately
* `parent_article` — the article of which this is a part or accessory
* `assembled_movement` — the complete, assembled movement
* `absence_of_heading` and `absence_of_subheading` — the rate that would apply without it
* `heading` — a named heading, for example `{"targets": ["2009"]}`
* `note` — a named U.S. note, for example `{"note": "U.S. note 3", "basis": "repair_value"}`
* `self_subheading` — this subheading's own rate, optionally with `delta_percentage`,
`plus_percentage`, or `originating_in` for a trade-agreement rate

We record the pointer. We do not follow it. Resolving "heading 2009" on your behalf would
mean choosing one of its children for you, which is a classification decision, not a rate
lookup.

88 duty rows moved from unparsed to reference. Four rows remain unparsed across the whole
schedule: the drawback clauses of `9801.00.70` and `9801.00.80`, which genuinely state no
rate.

Fix:

Compound duties no longer drop a term, and 201 rates now parse

Two correctness fixes and a large coverage improvement.

A duty carrying both a percentage and a per-unit component was classified from a
denormalised `compound_rate` flag rather than from the components themselves. When a
backfill set the components without the flag, the rate was demoted to `specific` and its
ad valorem side was silently dropped. Thirteen headings were affected, including
`9606.21.40.00` (buttons), whose rate is 4.6% plus 0.3 cents per line per gross.

Separately, ten watch and clock headings had been storing only the first two terms of a
three-term rate, which under-reported their duty.

The rate parser gained coverage for rate shapes it had always refused:

* Chapter 99 special-column rates qualified by a trade program, such as "$1.41/kg (PA)".
* Missing unit aliases: per metre, per linear metre, per pack, per article, per jewel,
per line per gross, per thousand pins.
* Rates with three or more terms, and rates charged on a metal content, a component of the
article, the fair retail value or the cost of repairs.
* The polarimeter-degree sugar formulas of Chapter 17, including their floors.
* Cross-references to another heading, subheading or U.S. note.
* Damage introduced by the source extraction, such as a soft-hyphen break inside a word.

Duties whose rate text contains a number and which we could not parse fell from 203 to 2.
A further 90 headings state their rate in words alone, with no number at all; those are
covered in V.1.0.7.

Fix:

Unreadable base duties are no longer reported as 0%

When a base duty could not be parsed, the API returned
`rate_type: "ad_valorem", percentage_component: null`, and `summary.notes` said
"Base duty is 0%" alongside `confidence.score: 1.0` and an empty `warnings` array. Any
client that reads a null percentage as zero computed a duty-free base. Our own AI tools
rendered those headings as "Free".

This affected 91 general-column headings, including beet sugar, and the lead, molybdenum,
tungsten and magnesium ores.

A base duty that is not a plain number now says so, on every endpoint including
`resolve_batch`:

* `summary.base_rate_known: false` — `applicable_ad_valorem_rate` excludes the base duty
and is a lower bound.
* `warnings[]` carries one of `BASE_RATE_UNPARSED` (we could not read it),
`BASE_RATE_NOT_CALCULABLE` (we read it but it needs inputs only you hold) or
`BASE_RATE_REFERENCE` (it defers to another heading or note), each with the source text
and, where relevant, `inputs_required` or `refers_to`.
* `confidence` gains a fourth factor, `base_rate`, which drops the score to 0.5.

A specific duty is no longer described as "Base duty is 0%" either. A rate of 68 cents per
head has no ad valorem component, but it is not a zero duty, and the note now says so.

`applicable_ad_valorem_rate` is unchanged in shape and remains a number.

Update:

New rate_type values: unparsed, reference and formula

`base_tariff.rate_type` previously returned only `ad_valorem`, `specific` or `compound`.
Three values have been added, and each one replaces a case that used to be reported
misleadingly as `ad_valorem` with a null rate:

* `formula` — we read the rate but cannot total it without inputs only you hold. Carries
`components[]`, `constraints[]`, `inputs_required` and `raw_rate`.
* `reference` — the heading defers to another heading, subheading or U.S. note. Carries
`refers_to` and `raw_rate`.
* `unparsed` — we could not read the rate at all. Carries the verbatim USITC text in
`raw_rate`.

If your integration switches on `rate_type`, add these three cases. A response with
`rate_type` of `formula`, `reference` or `unparsed` has a **null** `percentage_component`,
and a null percentage is not a zero percentage. A `formula` rate deliberately exposes no
scalar at all: for `1.7 cents/kg on lead content`, a `per_unit_component` of 0.017 would
invite you to multiply by total kilograms when the correct multiplicand is the lead
kilograms.

The API path is unchanged and remains `/api/v1`.

New Feature:

Structured rate components on base_tariff

Some US HTS headings carry a base duty that a single number cannot express. A watch
movement is dutiable at "36 cents each + 5.6% + 2 cents per jewel". Lead ore is dutiable
per kilogram **of lead content**, not per kilogram of ore. Beet sugar is dutiable per
kilogram, reduced for every polarimeter degree under 100, with a floor. Until now the API
had only two fields for a rate, `percentage_component` and `per_unit_component`, so these
headings had no honest representation.

`base_tariff` now carries five new fields:

* `components[]` — every term of the rate, as `{kind, value, unit, basis}`. `kind` is
`specific` or `ad_valorem`. `basis` appears only when the term applies to something
other than the entered value or the entered quantity, for example `lead_content`,
`battery`, `fair_retail_value` or `degree_under_100`.
* `constraints[]` — floors and caps, for example `{"kind":"floor","value":0.03143854,"unit":"kg"}`
or `{"kind":"cap","basis":"complete_movement"}`.
* `duty_calculable` — `false` when the duty cannot be totalled from entered value and
entered quantity alone.
* `inputs_required` — what you must supply when `duty_calculable` is `false`, for example
`[{"name":"polarimeter_degrees","unit":"degree"}]`.
* `refers_to` — for headings that name another heading or a U.S. note instead of stating a
rate, for example `{"kind":"heading","targets":["2009"]}`.

These fields are additive. `percentage_component` and `per_unit_component` keep their exact
meaning for ordinary ad valorem, specific and compound rates.

We describe these rates. We do not total them, and we do not follow a reference to another
heading. The jewel count of a movement, the copper content of an ore and the polarimeter
reading of a sugar shipment are facts only the importer holds. Guessing at them would
produce a confident wrong number.

New Feature:

AI Chat Assistant

We have added a new way to discover HTS codes, our AI agent trained on our entire database will be able to help you discover and understand HTS codes.

New Feature:

Organizations: Collaborate on HTS codes with your team

Invite and collaborate on hts codes by inviting your team to your TariffsAPI organization. Share your subscription API and AI credits

Fix:

Tariff Data Fixes: 8471 Exemption Restored & Cleaner API

Section 122 exemption restored for heading 8471, exempted measures now visible with suppression reasons, and duplicate duties removed.

Update:

New API Dashboard

We have refreshed the API dashboard to include more details around API credits, API keys usage and billing.

Resources:

HTS source on API response

We have added the official source on every API response. You can now always verify the official sources by yourself for every API call.

Fix:

Better Data Accuracy

We have increased our audit process to increase the data accuracy, including unique cases like the reversal of teh IEEPA tariffs under the current administration.

New Feature:

MCP server connection

Speak directly to TariffsAPI database through our MCP endpoint 🔥

New Feature:

Batch resolve endpoint

You can now query several rates at a time, up to 150 rates in one API call. This is perfect if you have high needs and product interations.

Update:

Country-Aware Tariff Resolution (Section 301, Trade Agreements & HTS Inheritance)

“Given an HTS code and a country of origin, what tariffs apply today?” - Try out the /resolve endpoint and our updated tariffs calculator.

Update:

Automatic inheritance when exact matches aren't found

querying 8541.10.00.80 will return tariffs from 8541.10.00 when the child has no tariffs.