HS Code & Tariff API
The HS Code & Tariff API returns HS/HTS commodity classification and duty data as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary hs_lookup endpoint returns a code's description, units and duty rates (general/MFN, special, column 2) for a destination, and you can classify a product from text, estimate landed_cost, check schedule status, batch-classify, browse, validate a code and compare cross-country. It is built for customs, cross-border e-commerce and trade-compliance workflows that need tariff data without a customs database. One ReefAPI key, one shared credit pool, the standard envelope.
Why a code returns no duty rate: declarable versus not
This is the single thing that confuses people, and it is not about how many digits your code has. A tariff line only carries rates if it is declarable — the level you could actually put on a customs entry. Everything above that is a classification bucket and correctly returns null rates plus its children. All rows measured against the UK schedule.
| Code | declarable | What comes back |
|---|---|---|
| 6403 (4-digit heading) | false | description, an explanatory note, and context[] listing 60 child lines. import_duty and vat are both null — there is no rate at this level to return. |
| 6403120000 (ski boots) | true | third-country MFN 8.0%, a VAT rate, preference_measures 56, the full section XII / chapter 64 / heading 6403 hierarchy, validity_start 1996-01-01, validity_end null, and a BTI link |
| 6109100000 (T-shirts) | false | ten digits and still no rates — digit count does not decide declarability |
| 8471300000 (portable computers) | true | duty 0.0%, VAT 20.0% |
| destination: UK | — | HMRC Online Trade Tariff, live. Codes are UK commodity codes. |
| destination: US | — | USITC Harmonized Tariff Schedule. HTS lines are a different national code space — a UK commodity code is not an HTS line below 6 digits. |
| destination: anything else | — | out of scope in v1. The EU is not covered and we say so rather than returning an approximation. |
Every response carries schedule_version and a disclaimer. These are the official published schedules, but a code is only legally binding when a customs authority says so — the bti_url on each declarable line is the route to a binding decision. Treat everything here as reference, not advice.
Real request and response JSON
Captured from the indexed primary action, hs_lookup, on .
{
"method": "POST",
"url": "https://api.reefapi.com/hs-code/v1/hs_lookup",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"code": "8471.30.0100",
"destination": "US"
}
}{
"ok": true,
"meta": {
"api": "hs-code",
"endpoint": "hs_lookup",
"mode": "live",
"latency_ms": 1407.7,
"record_count": 1,
"bytes": 16549,
"cache_hit": false,
"sources": [
"hts.usitc.gov"
],
"license": "US-HTS public-domain / UK-OGL",
"schedule_version": "2026HTSRev11"
},
"data": {
"destination": "US",
"code": "[redacted-phone]",
"query_code": "[redacted-phone]",
"record": {
"hts_code": "[redacted-phone]",
"hts_code_digits": "[redacted-phone]",
"indent": 1,
"description": "Portable automatic data processing machines, weighing not more than 10 kg, consisting of at least a central processing unit, a keyboard and a display",
"is_superior": false,
"units": [
"No."
],
"duty": {
"general_mfn": "Free",
"general_mfn_pct": 0,
"special": null,
"column_2": "35%",
"rate_note": null,
"additional_duties": null,
"quota_quantity": null
},
"footnotes": []
},
"context": [
{
"hts_code": "8471",
"description": "Automatic data processing machines and units thereof; magnetic or optical readers, machines for transcribing data onto data media in coded form and machines for processing such data, not elsewhere specified or included:",
"general_mfn": null
},
{
"hts_code": "[redacted-phone]",
"description": "Portable automatic data processing machines, weighing not more than 10 kg, consisting of at least a central processing unit, a keyboard and a display",
"general_mfn": "Free"
},
{
"hts_code": "[redacted-phone]",
"description": "Comprising in the same housing at least a central processing unit and an input and output unit, whether or not combined",
"general_mfn": "Free"
}
],
"schedule_version": "2026HTSRev11",
"source": "USITC HTS (hts.usitc.gov)",
"disclaimer": "Reference classification and rates from the official US (USITC HTS) and UK (HMRC Trade Tariff) schedules — informational only, NOT binding customs or legal advice. Landed-cost figures are estimates. Anti-dumping/countervailing duties, Section 301/232 additional tariffs, rules-of-origin, trade-preference eligibility, quotas and special programs are not fully modelled. Verify the final HS code and duty with a licensed customs broker before relying on it."
}
}What the HS Code & Tariff API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| hs_lookup | One HS/HTS/commodity code + destination (US|UK) → the official tariff line: description, units, MFN duty rate (+ parsed % when ad-valorem), VAT/GST, special/preference programs, footnotes, schedule version. A 4/6-digit code resolves to its most specific rate-bearing line and lists context. | Pricing teams call hs_lookup to get one HS/HTS/commodity code + destination (US|UK) → the official tariff line. | code, destination |
| classify | Product description (+materials) + destination → ranked candidate HS codes with a confidence band, reason, and review_required. Candidates are SUGGESTIONS for a human/broker to confirm — low-confidence candidates are clearly marked, never shown as a settled code. | Marketplace operators call classify to get product description (+materials) + destination → ranked candidate HS codes with a confidence…. | description, destination, materials, limit, min_confidence |
| landed_cost | HS code (or description → auto-classified) + destination + value (+origin, currency, shipping) → estimated duty + VAT/GST + total landed cost, with an explicit assumptions[] list and the disclaimer. A simple, honest estimate — NOT a customs quote. | Catalog enrichment teams call landed_cost to get hS code (or description → auto-classified) + destination + value (+origin, currency, shipping…. | destination, value, code, description, origin, ... |
| schedules_status | Freshness + coverage of the underlying official schedules (US HTS revision, UK tariff reachability) — so a caller knows how current the rates are and which markets are live. | Retail analysts call schedules_status to get freshness + coverage of the underlying official schedules (US HTS revision, UK tariff reachab…. | none |
| batch_classify | Up to 25 products in one call (discounted). Each item is classified (or looked up if a code is given) independently; one bad item never fails the batch. | Pricing teams call batch_classify to get up to 25 products in one call (discounted). | items, destination, limit, min_confidence |
| browse | Navigate the tariff tree without knowing a keyword: list the 21 WCO sections, drill a section → chapters, a chapter (2-digit) → headings, a heading (4-digit) → subheadings/lines, a subheading (6-digit) → national lines. Each node returns its parent breadcrumb + children. The browse companion to keyword `classify`. | Marketplace operators call browse to get navigate the tariff tree without knowing a keyword. | destination, node |
| validate | Check whether a code is a real, current tariff code in the chosen market — structural check (4/6/8/10 digits) PLUS live existence in the schedule. A well-formed but unknown code returns valid=false with the nearest valid parent + sibling suggestions, so a caller can correct a typo'd or expired code before filing. | Catalog enrichment teams call validate to check whether a code is a real, current tariff code in the chosen market. | code, destination |
| cross_country | One 6-digit HS subheading (the WCO-international level shared by every member country) → the universal HS6 description plus the matching US (USITC HTS) and UK (HMRC) national lines side by side, with each market's duty rate. The same product, compared across the markets we cover — first 6 digits identical worldwide, national 8/10-digit detail differs. | Retail analysts call cross_country to get one 6-digit HS subheading (the WCO-international level shared by every member country) → the…. | hs6 |
Call hs_lookup from your stack
curl -X POST https://api.reefapi.com/hs-code/v1/hs_lookup \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"code":"8471.30.0100","destination":"US"}'import requests
r = requests.post(
"https://api.reefapi.com/hs-code/v1/hs_lookup",
headers={"x-api-key": REEF_KEY},
json={
"code": "8471.30.0100",
"destination": "US"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/hs-code/v1/hs_lookup", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"code": "8471.30.0100",
"destination": "US"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.hs-code.hs_lookup with {"code":"8471.30.0100","destination":"US"}.Who uses this API and why
- Cross-border e-commerce tools call classify then landed_cost to quote duties and taxes at checkout.
- Trade-compliance teams use hs_lookup and validate to confirm the correct code and duty rate.
- Logistics products use cross_country to compare tariffs for a commodity across destinations.
Questions developers ask before integrating
I looked up a code and import_duty came back null. Why?
Because the code is not declarable. A measured lookup of UK heading 6403 returned declarable false with import_duty null, vat null and a note saying rates are published at the declarable level — plus context[] with 60 child lines to choose from. That is the answer, not a failure: a 4-digit heading is a category, and duty is set per specific line. Walk into context[] and pick the child that matches the goods, then look that up. Do not assume that adding zeros to a heading produces a valid line.
Does a 10-digit code always carry rates?
No. Measured, UK 6109100000 for T-shirts came back declarable false with null rates, while 6403120000 with the same digit count came back declarable true with an 8.0% duty. Declarability is a property of the tariff line in the schedule, not of its length, because some ten-digit codes are themselves intermediate nodes. Always branch on the declarable boolean and never on len(code).
How reliable is the classify action?
Treat it as a shortlist, never as an answer. It is a relevance heuristic over the tariff text — the response says so in confidence_note — and the score reflects search rank, not correctness. A measured UK classification of 'leather shoes for men, leather upper' returned a top candidate at confidence 0.95 with confidence_band 'high' and review_required false, and that candidate was a sports-footwear line: tennis shoes, basketball shoes, gym shoes. High confidence there means a strong text match, not the right code. Use it to narrow to a chapter, then confirm the line with hs_lookup and a broker.
How is the landed cost actually calculated?
Transparently, and the assumptions travel with the answer. A measured UK estimate on a 1,200 value with 80 shipping and an 8.0% duty line returned estimated_duty 96.0 — eight percent of the customs value alone, not of value plus shipping — with VAT computed on value plus shipping plus duty, and total_landed_cost 1,376.0 in a breakdown you can check by hand. Read assumptions[] every time: it states that the general third-country MFN rate was applied for the origin you gave, that trade-preference (FTA) rates, anti-dumping duty and US Section 301/232 surcharges are NOT applied automatically, and that no currency conversion happens — all figures stay in whatever unit you passed.
What does preference_measures tell me?
How many preferential rate measures exist on that line, as a count — 56 on the measured ski-boot code. It is a signal, not a rate: it tells you there is real money to be found if your goods qualify under an FTA, a GSP scheme or a quota, and that the MFN figure you were quoted is the worst case. It does not tell you which ones you qualify for, and we do not guess at that; origin rules are the part that needs a human.
What does an UPSTREAM_HTTP error mean here, and why does retryable differ?
It means the government schedule itself did not answer. Observed against the US side: hs_lookup returned UPSTREAM_HTTP with 'no HTS data for heading 8471 (status 503)' and retryable true, while landed_cost on the same code returned the same underlying failure with retryable false. The difference is deliberate — a lookup is worth retrying because the .gov service may recover in seconds, but a landed-cost estimate has no meaning without a tariff line, so retrying the whole calculation is wasted work. Fix the lookup first, then recompute. During an outage classify can also return zero candidates for that market, which is an empty index rather than an unclassifiable product.
Can I use this to file a customs declaration?
No, and every response says so. The data is pulled from the official published schedules — USITC HTS for the US and HMRC's live Trade Tariff for the UK, both named in source and pinned by schedule_version — but a classification is only binding when the customs authority issues it. Each declarable UK line carries a bti_url pointing at the binding-decision process. Use this to estimate, to compare markets and to check a broker's work; do not use it as the sole basis for an entry.
What is in the hierarchy object?
The full path above your code, which is what you need to explain a classification to somebody. A measured ski-boot lookup returned section XII with its full title, chapter 64 'Footwear, gaiters and the like; parts of such articles', and heading 6403 'Footwear with outer soles of rubber, plastics, leather or composition leather and uppers of leather'. Sections are Roman numerals and chapters are two digits, which is worth knowing before you try to sort them as integers. Alongside it, validity_start and validity_end date the line — a null validity_end means the line is currently in force.
What is the HS Code & Tariff API?
HS Code & Tariff API is a ReefAPI endpoint group for hs code & tariff It returns live JSON through POST requests under /hs-code/v1.
Is the HS Code & Tariff API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. HS Code & Tariff calls use the same shared credit balance as every other ReefAPI engine.
Do I need a HS Code & Tariff login or account?
No login to HS Code & Tariff is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the HS Code & Tariff data?
The page example is captured from a live hs_lookup call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the HS Code & Tariff API use?
HS Code & Tariff actions currently cost 1-8 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call HS Code & Tariff from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call hs-code actions with the same key, credit pool and JSON envelope used by normal REST requests.