US Tariff Schedule Scraper
Scrape the US Harmonized Tariff Schedule: HTS codes, descriptions, units of quantity, general, special and column 2 duty rates, and the edition on every row.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
chapters,hsCodes,searchTerms(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.002 per tariff line = $2 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Tariff line scraped | One HTS tariff line with its duty rates and the edition it came from. Edition mismatches stop the run before it spends. | $0.002 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-20, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
chapters | Chapters of the tariff schedule to read, as numbers: 61 is knitted apparel, 84 is machinery, 87 is vehicles, 99 holds the temporary modifications. Ranges work too - write 61-63. Chapter 77 is reserved in the Harmonized System and has no lines. | array |
hsCodes | Specific codes, at any depth. 8471 gives you the whole heading, 8471.30 the subheading, 6101.20.00.10 that one statistical line. Dots are optional. The run reads the chapter each code sits in and keeps the lines that start with it. | array |
searchTerms | Words to look up in the schedule's own index, like "laptop" or "olive oil". The matching codes come back as full rows with their duty rates and the description path above them. The index is broad - searching "cotton shirt" also returns cotton seed - so check the descriptions. | array |
allChapters | Reads every chapter from 01 to 99 in order. That is about 29,900 numbered lines, so raise the row limit to match or you will get the first slice of it. This costs you one charge per row. | boolean |
includeLines | The schedule is a tree. "all" returns every numbered line, including the 4-digit headings and 6-digit subheadings that hold the structure but print no rate. "withDutyRate" drops those and keeps the 8- and 10-digit lines. "statisticalOnly" keeps just the 10-digit lines, the ones that go on an entry. | string |
maxItems | How many tariff lines to return. Keep it low while you are testing - you pay per row. The whole schedule is about 29,900 numbered lines, and the hard ceiling is 40,000. | integer |
requireEdition | Optional. Put an edition name here, like 2026HTSRev19, and the run will refuse to return anything if the live schedule is a different edition. Useful on a schedule, where a revision landing overnight would otherwise change your rates without anyone noticing. Leave it empty to take whatever is current. | string |
proxyUrls | Leave this empty for a normal run. Fill it in only if you want the traffic to leave through proxy servers you already pay for, one URL per line, in the form http://user:pass@host:port. | array |
What you get
A structured dataset — each result includes fields like:
htsnolineLevelchapterheadingdescriptionunitOfQuantitygeneralRateEffectivespecialRateEffectivecolumn2RateEffectiveratesAreInheritedrateSourcedutyRateResolvedhtsEditionhtsUrlExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Developer & Research Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
GitHub Scraper
Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.
Stack Overflow / Stack Exchange Scraper
Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.
Package Registry Scraper (npm + PyPI)
Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.
arXiv Scraper
Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.
OpenAlex Scholarly Works Scraper
Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.
Crossref Scholarly Works Scraper
Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.
Where this tool sits
- Categories
- Developer & Research Tools
US Tariff Schedule Scraper: HTS codes, duty rates and units, with the edition on every row
Give it chapters, HTS codes or search terms, and it reads those lines of the US Harmonized Tariff Schedule from the USITC's own published data. Each row is one line: the HTS code, its description and full description path, the unit of quantity, the general, special and column 2 rates exactly as printed, the rate that applies when a line inherits it, and the edition it came from. The awkward part: an empty rate on a line does not mean duty-free, and only the live edition is published, so a rate you pulled in March may not be the one in force in September.
| Input | Chapters or chapter ranges, HTS codes at any depth, search terms, or the whole schedule |
| Output | One row per tariff line |
| Ceiling | 40,000 rows, 200 codes and 10 search terms per run |
| Account needed | None |
| Price | $2.00 per 1,000 tariff lines, flat on every plan |
🔍 What US Tariff Schedule Scraper does
The edition is read before a single line, and every row carries it, so a dataset dates itself. It is read again at the end, and if a revision landed mid-run you get a free row saying so. On a schedule, requireEdition makes the run return nothing rather than a different edition.
Then the rates. The schedule states a rate once and lets the lines beneath it inherit, so a 10-digit line often prints nothing in its rate columns. In edition 2026HTSRev19, 11,836 of the 29,860 numbered lines carried no rate of their own. Read those blanks as zero and you price about two lines in five at no duty when they are dutiable. So each row keeps both readings apart:
| Field | What it is |
|---|---|
generalRate, specialRate, column2Rate | Exactly what this line printed. null when it printed nothing |
generalRateEffective, specialRateEffective, column2RateEffective | The rate that applies, copied verbatim from wherever it was stated |
ratesAreInherited, rateSource | Whether it came from a line above, and the HTS code it was read off |
dutyRateResolved, rateNote | false when no line above prints a general rate, with the reason in plain words |
A rate is only inherited from a line whose code starts the row's own code. Rates are never parsed into numbers. You get 15.9%, Free, 1¢/kg, 3.7¢/kg on drained weight, and in chapter 99 whole sentences such as The duty provided in the applicable subheading + 25%. Turning those into a number is a judgement about your goods.
Each chapter is checked before it is used. One that comes back empty, or carrying lines from another chapter, becomes a free row instead of a charge.
📥 What you give it
| Field | If you leave it out | What it does |
|---|---|---|
chapters | none | Chapter numbers 1 to 99, or ranges such as 61-63. 61 is knitted apparel, 84 machinery, 87 vehicles, 99 the temporary modifications. Chapter 77 is reserved and gets a free row saying so. |
hsCodes | none | Codes at any depth, 2 to 10 digits, dots optional, up to 200. 8471 is a whole heading, 6101.20.00.10 one line. |
searchTerms | none | Words looked up in the schedule's own index, up to 10. The index is broad: cotton shirt also finds cotton seed, so read the descriptions. |
allChapters | false | Reads every chapter, 01 to 99. Raise maxItems to match or you get the first slice. |
includeLines | all | all, withDutyRate (8- and 10-digit lines only) or statisticalOnly (the 10-digit lines that go on an entry). |
maxItems | 100 | 1 to 40,000 rows. |
requireEdition | any edition | An edition name such as 2026HTSRev20. If a different one is live, the run reads nothing and charges nothing. |
proxyUrls | not used | Your own servers to send the traffic through, as http://user:pass@host:port. Leave it empty unless you need that. |
One thing about combining them. Give chapters together with hsCodes or searchTerms and you get the whole chapters, with the codes only filling in matchedQuery. To get just the lines you named, leave chapters empty.
{
"hsCodes": ["8471.30", "6101.20"],
"searchTerms": ["olive oil"],
"includeLines": "statisticalOnly",
"maxItems": 200
}
An input with nothing in it, such as {} sent through the API, returns one free sample row so you can see the shape first.
📤 What you get back
A real row from a real run. Look at the rates: this line printed none, and the 15.9% it carries was read off 6101.20.00 above it.
{
"ok": true,
"charged": true,
"recordType": "tariffLine",
"htsEdition": "2026HTSRev20",
"htsEditionTitle": "Revision 20 (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": null,
"htsUrl": "https://hts.usitc.gov/search?query=6101.20.00.10",
"scrapedAt": "2026-09-30T02:28:10.927Z"
}
| Field | What it is |
|---|---|
htsEdition, htsEditionTitle | The edition the row came from, so two pulls a month apart can be told apart without notes. |
htsno, lineLevel | The code as published, and its level: heading (4 digits), subheading (6), tariffLine (8) or statistical (10). |
chapter, heading, subheading | The code's own leading digits, split out for grouping. |
description, descriptionPath | The line's own text, often a fragment like Men's (334), and the whole branch joined with > so a deep line reads on its own. |
unitOfQuantity, units | The published unit as text, and the same thing split into an array. Many lines carry two. |
additionalDuties, footnotes | Extra duty provisions, mostly chapter 99 safeguards, and the publisher's own footnotes, untouched. |
matchedQuery | Which of your codes or search terms picked the row. |
htsUrl | The line on hts.usitc.gov, for when someone wants to check. |
🧾 Reading the output
Three kinds of row can share a dataset, and charged is the field that separates them.
| Row | How to spot it | Charged |
|---|---|---|
| A tariff line | charged: true | yes |
| The sample | _sample: true, charged: false | no |
| A diagnostic | _diagnostic: true, charged: false, an errorCode | no |
The sample row also says recordType: "tariffLine", so filter on charged == true, not on recordType.
| Code | What it means |
|---|---|
NO_RESULTS | Several cases, and details says which: nothing matched, a search term found nothing, chapter 77, a chapter that came back empty, a requireEdition that is not the live one, or a revision that landed while the run was reading. |
BAD_INPUT | Something in chapters or hsCodes is not a chapter number or an HTS code. |
NETWORK | The schedule could not be read. If that happens at the edition check, nothing is read at all. |
TIME_BUDGET | The run ran out of time before a chapter was read. |
▶️ How to run it
1. Open US Tariff Schedule Scraper and click Try for free. 2. Type chapter numbers into Chapters, or clear it and use HTS codes or Search terms. The form arrives with chapter 61. 3. Choose Which lines to return. "Statistical lines only" gives you the codes that go on an entry. 4. Set Maximum rows, or tick Read the whole schedule and raise it to match. 5. Click Start, then download the dataset as CSV, JSON or Excel.
💰 How much does it cost?
$2.00 per 1,000 tariff lines, which is $0.002 a line, flat on every Apify plan. An average chapter holds around 300 lines, which comes to roughly 60 cents, and the whole schedule of about 29,900 lines to roughly $60.
You pay for tariff-line rows and nothing else per row. The sample row and every diagnostic row are free, including chapter 77, an empty chapter, a search term that finds nothing and a requireEdition refusal. Lines in chapters 98 and 99 that resolve to no rate are still charged, because they are still the lines you asked for. If you set a maximum charge on the run, it stops at that figure instead of going past it.
💡 What people use it for
- Checking the duty on a product before pricing an import, with the rate and the line it came from on one row.
- Keeping your own history. Only the live edition is published, so a pull after each revision is the only archive you will have.
- Seeing what a revision moved, by comparing two pulls on
htsnoplusgeneralRateEffective. - Loading the schedule into a spreadsheet, where
descriptionPathmakes a deep line readable on its own.
🚧 What it does not do
- No classification. Which line your goods fall under is your call, or your broker's.
- No advice, customs, legal or otherwise.
- Only the live edition. There is no way to ask for last year's rates.
- No notes. General, section and chapter notes are prose documents, not data. Chapter titles are not returned either.
- Only the US schedule. No other country's tariff.
- No duty calculation. No landed cost, freight, trade-remedy stacking or rules of origin.
- Chapters 98 and 99 carry lines with no rate. In edition 2026HTSRev19, 1,169 of their lines resolved to no general rate, because the treatment comes from a note rather than a rate column. They arrive with
dutyRateResolved: falseand arateNote.
🧭 Which trade data scraper do you need?
| If you want | Use |
|---|---|
| US tariff lines and duty rates | This one |
| Who imports what, from US customs shipment records | ImportYeti Scraper |
| Wholesale products, price ranges and MOQs from suppliers | Alibaba.com Scraper |
| US industrial suppliers by product and location | Thomasnet Suppliers Scraper |
❓ Questions people ask
What is the HTS? The Harmonized Tariff Schedule of the United States: the codes every imported good is classified under and the duty attached to each. The US International Trade Commission publishes it.
How many lines are there? In edition 2026HTSRev19 there were 29,860 numbered lines: 961 headings, 2,093 subheadings, 6,857 eight-digit lines and 19,949 statistical lines. Revision 20 is live now and has not been recounted.
Can I get the Section 232 and 301 duties? Chapter 99 holds the temporary modifications and it is included: {"chapters": ["99"]}. Those lines often state the treatment as a sentence rather than a percentage, and you get that sentence.
Can I run it on a schedule? Yes. Compare runs on htsno plus generalRateEffective to see what moved, and watch htsEdition for when a revision landed.
🆘 If something breaks
Open the Issues tab on the actor page and send the run ID with the input you used. The errorCode and details on a diagnostic row usually name the reason already.