US Tariff Schedule Scraper - HTS Codes & Duty Rates avatar

US Tariff Schedule Scraper - HTS Codes & Duty Rates

Pricing

from $2.00 / 1,000 tariff line scrapeds

Go to Apify Store
US Tariff Schedule Scraper - HTS Codes & Duty Rates

US Tariff Schedule Scraper - HTS Codes & Duty Rates

One row per line of the US Harmonized Tariff Schedule: HTS code, description, unit of quantity, general rate of duty, special rates with the countries they cover, and the column 2 rate. Every row names the edition it came from. Keyless, no browser, no account.

Pricing

from $2.00 / 1,000 tariff line scrapeds

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

US Harmonized Tariff Schedule Scraper: HTS codes, duty rates, units of quantity

One row per published line of the US Harmonized Tariff Schedule: the HTS code, what it covers, the unit of quantity, the general rate of duty, the special rates and the countries they apply to, and the column 2 rate. Read straight from the USITC's own JSON, keyless, no browser, no account.

Read this part first, because it is the part that can hurt you. The schedule is revised several times a year, and a rate you pulled in March may not be the rate in force in September. So every row names the edition it came from (2026HTSRev19, say), and the run reads that edition name off the publisher before it reads a single line. Only the live edition is published, so this Actor can only give you the live one. If you are running this on a schedule and a revision landing overnight would quietly change your numbers, put the edition you expect in requireEdition and the run will return nothing rather than hand you a different one.

And the other thing worth knowing before you buy anything: an empty duty rate does not mean duty-free. More on that below, because it is the single easiest way to read this data wrong.

This Actor returns tariff lines as the USITC published them. It does not tell you how to classify goods, and nothing in the output is advice.

What one row looks like

A real row from a real run:

{
"ok": true,
"charged": true,
"recordType": "tariffLine",
"htsEdition": "2026HTSRev19",
"htsEditionTitle": "Revision 19 (2026)",
"htsno": "6101.20.00.10",
"lineLevel": "statistical",
"chapter": "61",
"heading": "6101",
"subheading": "6101.20",
"headingDescription": "Men's or boys' overcoats, carcoats, capes, cloaks, anoraks (including ski-jackets), windbreakers and similar articles, knitted or crocheted, other than those of heading 6103:",
"indent": 2,
"description": "Men's (334)",
"descriptionPath": "Men's or boys' overcoats, carcoats, capes, cloaks, anoraks (including ski-jackets), windbreakers and similar articles, knitted or crocheted, other than those of heading 6103: > Of cotton > Men's (334)",
"unitOfQuantity": "doz., kg",
"units": ["doz.", "kg"],
"generalRate": null,
"specialRate": null,
"column2Rate": null,
"generalRateEffective": "15.9%",
"specialRateEffective": "Free (AU,BH, CL,CO,IL,JO,KR, MA,OM, P,PA,PE,S,SG)",
"column2RateEffective": "50%",
"ratesAreInherited": true,
"rateSource": "6101.20.00",
"dutyRateResolved": true,
"rateNote": null,
"additionalDuties": null,
"footnotes": [],
"matchedQuery": "6101.20.00",
"htsUrl": "https://hts.usitc.gov/search?query=6101.20.00.10",
"scrapedAt": "2026-09-20T10:26:26.276Z"
}

Look at that row again. generalRate is null, because the printed line for 6101.20.00.10 genuinely has nothing in the general column. generalRateEffective is 15.9%, and rateSource says where that came from: the line above it, 6101.20.00.

That is how the schedule is printed. A rate is stated once and the statistical lines underneath it inherit it. In the current edition, 11,836 of the 29,860 numbered lines carry no rate of their own. If you read the raw export and treat a blank as zero, you will price roughly two lines in five at no duty when they are dutiable.

So each row gives you both readings, and keeps them apart:

FieldWhat it is
generalRate, specialRate, column2Rateexactly what this line printed. null when it printed nothing
generalRateEffective, specialRateEffective, column2RateEffectivethe rate that applies, copied verbatim from wherever it was stated
rateSourcethe HTS code that string was read off
ratesAreInheritedtrue when it came from a line above rather than this one
dutyRateResolvedfalse when no line anywhere above prints a general rate
rateNotewhy, in plain words, when dutyRateResolved is false

Rates are shipped as strings, byte for byte as published, and are never parsed into numbers. You will see 15.9%, Free, 1¢/kg, 68¢/head, 3.7¢/kg on drained weight, and in chapter 99 things like The duty provided in the applicable subheading + 25%. Turning that into a number is a judgement call about your goods, and it is not one this Actor is going to make on your behalf.

Input

Four ways to pick lines, and they combine:

{
"chapters": ["61", "62"],
"hsCodes": ["8471", "6101.20.00.10"],
"searchTerms": ["olive oil"],
"includeLines": "withDutyRate",
"maxItems": 500
}
FieldWhat it does
chaptersChapters to read, as numbers. 61 is knitted apparel, 84 machinery, 87 vehicles, 99 the temporary modifications. Ranges work: 61-63. Chapter 77 is reserved in the Harmonized System and holds nothing; ask for it and the run says so instead of returning silence
hsCodesCodes at any depth. 8471 gives the whole heading, 8471.30 the subheading, 6101.20.00.10 that one line. Dots optional. The run reads the chapter each code sits in and keeps the lines starting with it
searchTermsWords looked up in the schedule's own index. The codes it returns come back as full rows with their rates and description path. The index is broad (searching cotton shirt also returns cotton seed), so read the descriptions rather than trusting the match
allChaptersReads every chapter, 01 to 99, in order. About 29,900 numbered lines. Raise maxItems to match or you get the first slice
includeLinesall keeps every numbered line. withDutyRate drops the 4- and 6-digit structure lines and keeps the 8- and 10-digit ones. statisticalOnly keeps just the 10-digit lines, the codes that go on an entry
maxItemsHow many rows to return. Ceiling 40,000
requireEditionOptional. Name an edition and the run refuses to return anything if the live schedule is a different one
proxyUrlsLeave empty unless you want traffic to leave through servers you already pay for

Run it with empty input and you get one free sample row showing the output shape. Nothing is charged for that.

Output

Every row carries htsEdition and htsEditionTitle, so a dataset is self-dating and two pulls taken a month apart can be told apart without keeping notes.

FieldNotes
htsnothe code as published, dotted
lineLevelheading (4 digit), subheading (6), tariffLine (8), statistical (10)
chapter, heading, subheadingthe code's own leading digits, split out for grouping
headingDescriptionthe 4-digit heading this line sits under
descriptionthe line's own text, which on a deep line is a fragment like Men's (334)
descriptionPaththe whole branch joined with >, which is what makes a deep line readable on its own
unitOfQuantitythe published unit, as text. Many lines carry two, like doz., kg
unitsthe same thing as an array, if you would rather split it yourself
additionalDutiespresent on 512 lines in the current edition, all of them safeguard provisions in chapter 99
footnotesthe publisher's own footnote objects, untouched
matchedQuerywhich of your codes or search terms picked this row
htsUrlthe line on hts.usitc.gov, for when someone wants to check

Three kinds of row land in the dataset and they are easy to tell apart:

  • Real rows carry "charged": true and "recordType": "tariffLine". One billed event each.
  • The sample row carries "_sample": true and "charged": false. There is exactly one, only when the input picked nothing.
  • Diagnostic rows carry "_diagnostic": true, "charged": false and an errorCode. A chapter that came back empty, a code that was not a code, a search term that matched nothing, a request the publisher refused. None of them is billed.

Filter on charged == true and the row count equals the number of events you paid for. The dataset is its own invoice.

Price

$2.00 per 1,000 tariff lines, plus a $0.0005 start fee per run. Flat on every plan, free or paid, with no volume tiers and no minimum spend. One event per tariff line written to the dataset is the only thing metered per row.

Nothing else is charged. The sample row is free. Diagnostic rows are free. A chapter that comes back empty is free, a search term that matches nothing is free, and a request the publisher refuses is free. A run that finds nothing bills you for no rows at all.

A full chapter runs a few hundred lines, so most single-chapter pulls land under half a cent.

How it works

The USITC publishes the schedule as JSON from its own host. No key, no session, no anti-bot, no browser. The run reads one chapter per request, which keeps its memory flat whether you asked for one chapter or all ninety-eight, and holds the rows from a single chapter at a time rather than the whole schedule.

Before any of that it reads the publisher's edition endpoint, so the edition on every row is the one that was live at the moment the run started rather than something assumed. It reads it again at the end, and if a revision landed while the run was going you get a free row saying so. A whole-schedule pull takes the better part of an hour, which is long enough for that to happen to somebody.

Each chapter is checked before it is used. The publisher answers a range it does not recognise with HTTP 200 and an empty list. A status code cannot see that, so the run checks that lines came back and that they belong to the chapter that was asked for. Anything that fails produces a free diagnostic row and no charge.

The inheritance walk carries one more check that is worth knowing about. When a rate is taken from a line above, that line's HTS code has to be a genuine leading prefix of the row's own code. In the current edition all 11,836 inheritances satisfy that. If one ever does not, the row ships with no effective rate and says so, rather than showing you a number from somewhere else in the tree.

Limitations: what this does not do

  • It does not classify anything. It returns the schedule as published. Which line your goods fall under is a question this Actor has no opinion about.
  • It does not give advice, customs, legal or otherwise, and nothing in the output should be read as any.
  • Only the current edition. The publisher serves the live revision and nothing else, so there is no way to ask this for last year's rates. If you need history, pull on a schedule and keep what you get. htsEdition on every row is what makes that work.
  • No general notes, section notes or chapter notes. Those are the prose parts of the schedule and they are published as documents, not as data. Chapter titles are not returned either; you get the chapter number and the heading text.
  • No Canadian, EU or other national tariff. This is the US schedule. Canada publishes its Customs Tariff as chapter PDFs, with a different set of rate columns, so it is not something that can be folded into these rows honestly.
  • No duty calculation. No landed cost, no freight, no trade-remedy stacking, no rules of origin. The rate strings come to you as text precisely so nobody has to trust arithmetic they did not do.
  • Chapters 98 and 99 are returned, but 1,169 of their lines resolve to no general rate at all. That is how they are printed: the treatment comes from a US note attached to the subchapter rather than from a rate column. Those rows say dutyRateResolved: false and carry a rateNote explaining it, and they are still charged, because they are still the lines you came for.
  • The search index is the publisher's, not ours. It is broad and it ranks oddly. It is good for finding candidates and bad for trusting blindly.
  • A whole-schedule pull is slow. Rows land at roughly eleven a second, so all 29,860 takes about three quarters of an hour. Reading the source is not the slow part. Writing the rows out one at a time is. A single chapter is a couple of minutes at most, and most people want a chapter.

FAQ

What is the HTS? The Harmonized Tariff Schedule of the United States: the list of codes every imported good is classified under, and the duty rate attached to each one. It is published by the US International Trade Commission.

Why does a row have a rate when the line itself is blank? Because the schedule states a rate once and lets the lines beneath it inherit. generalRate shows what the line itself printed and generalRateEffective shows what applies, with rateSource naming where it was read from.

How many lines are there? 29,860 numbered lines in edition 2026HTSRev19: 961 four-digit headings, 2,093 six-digit subheadings, 6,857 eight-digit lines and 19,949 ten-digit statistical lines. Set allChapters and raise maxItems to pull the lot.

Can I get the Section 232 and 301 duties? Chapter 99 holds the temporary modifications, and it is included. Read it with {"chapters": ["99"]}. Be aware those lines often state the treatment as a sentence rather than a percentage, and they come to you as that sentence.

Which edition will I get? Whichever is live when the run starts. It is named on every row. Use requireEdition if you need the run to refuse anything else.

Do I need an API key for the source? No. The schedule is public and keyless. You need nothing beyond this Actor.

What happens if a chapter fails? You get one free diagnostic row naming the chapter and saying what went wrong, the run carries on to your other chapters, and you are not billed for the one that failed.

Can I run it on a schedule? Yes. Nothing is held between runs. Diff on htsno plus generalRateEffective to see what moved, and on htsEdition to see when a revision landed.

Is the output stable enough to use as a key? htsno is stable within an edition and mostly stable across them, but lines do get added, split and withdrawn at each revision. Keep htsEdition alongside it.