# US HTS Tariff Rates & Revision Watch for SKUs (`althorlabs/hts-revision-diff`) Actor

Enter your SKUs and their existing 8- or 10-digit US HTS codes. Get current USITC tariff rows on the first run. Reuse the same private snapshot store to see code and rate changes on later releases.

- **URL**: https://apify.com/althorlabs/hts-revision-diff.md
- **Developed by:** [Althor Labs](https://apify.com/althorlabs) (community)
- **Categories:** Business, E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 successful sku results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## HTS current rates and release watcher

This Actor returns current USITC HTS descriptions, units, and general, special, and other rates for SKU and already-classified HTS code pairs you submit. It stores each captured release as an immutable snapshot in a named Apify key-value store. After a later release becomes current, a run compares the selected codes with the latest earlier release stored in that KVS.

**Pricing:** USD 0.004 per successful SKU result row and USD 0.02 per selected change row with active pay-per-event pricing. There is no Actor-start fee. These prices include standard-run usage; no separate run-usage fee is passed to users. Post-run storage access may incur separate Apify charges.

The Actor does not classify products, recommend HTS codes, infer successor codes, or provide customs advice. Replace the runnable example SKUs with your own list before using results operationally.

### Input

`skuItems` is an array of objects with a `sku` label and an already-classified 8- or 10-digit `htsCode`. Dots are accepted. The Actor does not determine whether a submitted classification is correct.

`snapshotStoreName` names a persistent KVS in the Apify account that runs the Actor. Use the same name on later runs for this SKU list and a separate private store for each customer or SKU list. Keep the store unshared unless those users should see the stored SKU mappings and HTS snapshots. Named stores persist until removed in Apify storage management; this Actor does not delete stored snapshots.

Structured 9903 heading-line comparisons are available for codes you submit. An independent check scored 200 of 200 changes correctly across commodity-code, rate-change and structured 9903 cases, including 60 of 60 structured 9903 heading-line cases. This applies only to submitted heading-line codes; the Actor does not process U.S. Note text or PDFs, compare an entire chapter, classify products, or provide duty advice.

The default input is a runnable example, not a customer classification:

```json
{
  "snapshotStoreName": "hts-revision-watch-demo",
  "skuItems": [
    {
      "sku": "DEMO-8DIGIT",
      "htsCode": "0101.21.00"
    },
    {
      "sku": "DEMO-10DIGIT",
      "htsCode": "0101.21.00.10"
    }
  ],
  "include9903": true
}
```

### Run behavior

Each run reads the current release metadata. If that release has no stored snapshot, the Actor downloads the current export, checks the release metadata again, and stores the export only if the same release and effective date remain current. A source refusal, malformed response, or release change stops capture and leaves earlier snapshots untouched.

The first stored release is a baseline with `comparisonStatus: baseline_only`; it returns current rates without claiming a release change. Later runs on the same release return the saved rates with `comparisonStatus: no_new_release`. When a different release is confirmed current and an earlier release snapshot is available, the Actor compares submitted codes and returns `comparisonStatus: compared_new_release`. Monitoring occurs only when the Actor runs. The Actor does not create or change an Apify schedule.

#### Set up a recurring check and email

Use one saved Apify Task for each customer and SKU list. Run the Task once before scheduling it; that first run captures the baseline. Apify schedules can run Actors or Tasks, support a selected timezone, and are disabled when first created. See [Apify schedules](https://docs.apify.com/actors/running/schedules).

1. In the Actor input, replace the demo SKUs with your own SKU labels and already-classified HTS codes. Choose a unique `snapshotStoreName` for this list. Use a separate private store for each customer or list, and keep it private.
2. Save this input as an Apify Task, then run the Task once. Check the run's default dataset and confirm `watchStatus: ready_for_next_run` and `comparisonStatus: baseline_only`. This is the starting snapshot, not a change alert.
3. Create a schedule for that Task. Choose **Daily**, set the time and an explicit timezone, review the next run times, and enable the schedule. Keep the Task input unchanged so every run uses the same `snapshotStoreName` and SKU list. A daily check can incur charges even when the current release has not changed.
4. In the Task's **Integrations** tab, add Apify's [Send Email](https://apify.com/apify/send-mail) integration for successful runs. Set the recipient to an address you control in the same Apify account. Include these private Console links in the message:

   ```text
   Run: https://console.apify.com/view/runs/{{resource.id}}
   Dataset: https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}
   ```

   These links require Console access to the account. Do not make the dataset public or add an API token to a link. Send Email accounts on Apify's free plan can send only to the address associated with that account; check the [Send Email listing](https://apify.com/apify/send-mail) for its current charges and recipient limits.
5. Open the report linked in the email and review `comparisonStatus` and `watchStatus`. A successful-run email can contain `no_new_release` or a paused/unavailable source result as well as a new-release comparison. A successful run or email alone does not mean that rates changed or that the source is fresh. Apify's schedule notifications can also email when a scheduled run cannot start; for run failures or timeouts, add a separate Send Email integration for those events.

Every scheduled run produces a dataset record. The Actor charges USD 0.004 for each billable successful SKU result row, including baseline and `no_new_release` rows, plus USD 0.02 per selected change row. For 100 billable SKU rows on each daily run, budget about USD 12 per 30 days before change rows, storage, and Send Email charges. Unscored rows are not billed. Choose a frequency that fits your review needs and cost limit.

For agent-driven runs, use the same saved Task and preserve the exact SKU list and `snapshotStoreName`. Use a separate Task and private store for each customer or list. Read the Task's dataset and branch on `comparisonStatus` and `watchStatus`; do not treat a successful run as proof of a new release or available comparison. See [integrating Actors via API](https://docs.apify.com/integrations/actors/integrating-actors-via-api) for Apify's integration inputs and run data variables.

If the source refuses a request, the run pauses with `watchStatus: paused`, `comparisonStatus: unavailable`, empty current rates and changes, and a source error in `unscored` and `source`. For example:

```json
{
  "watchStatus": "paused",
  "comparisonStatus": "unavailable",
  "currentRates": [],
  "changes": [],
  "unscored": [
    {
      "record": "source",
      "reason": "current_export_unavailable",
      "detail": "Source request failed."
    }
  ],
  "source": {
    "status": "error",
    "error": "current_export_unavailable"
  }
}
```

### Sources and limits

The Actor makes ordinary HTTPS GET requests only to these routes:

- [USITC current release list](https://hts.usitc.gov/reststop/releaseList)
- [USITC current full export](https://hts.usitc.gov/reststop/exportList?from=0101\&to=9999\&format=JSON\&styles=false)

The Actor rejects redirects and does not use other hosts, archive URLs, PDFs, search routes, proxy configuration, header rotation, or retry transport. Each request has a 20-second timeout. The release list is capped at 3 MiB and the current export at 32 MiB.

### Output

Each dataset record includes the current release, selected current-rate rows, comparison status, changes, unscored items, source URLs, snapshot KVS key and digest, and processing time. In a hosted run, the selected 8-digit code `0101.21.00` returned a `general` value of `Free`. The selected 10-digit code `0101.21.00.10` returned a blank `general` value and included the exact 8-digit parent row under `parent8DigitContext`, marked as context only. The Actor does not copy the parent rate to the 10-digit row or assert which rate applies.

Billing uses that same aggregate dataset record. One SKU event is charged for each `skuResults` row with status `baseline`, `no_new_release`, `changed`, `no_change`, or `comparison_unavailable` when it has a selected current-rate object or a selected change. A returned rate row remains billable if release comparison is unavailable. A removed code can count when its removal is reported. One change event is charged for each top-level `changes` row also returned for a billable SKU result. Duplicate SKU codes are charged once per SKU result row and once per distinct change row. Input, storage, or source failures and unscored rows are free.

The complete report is saved before billing. Apify's per-run spending limit can reduce the charged event count without truncating the stored report. If billing fails after the report is saved, the run fails and the charge is not automatically retried. Check the saved output and charged events before retrying the whole run.

Snapshots use content-addressed keys, so captured data for a release cannot be overwritten. A later run compares a newly current release only with the latest earlier release stored in the same KVS. Conflicting content versions for one release are reported without selecting either version. Use a private store for each customer or SKU list.

### Technical notes

The storage adapter uses the pinned Apify Python SDK `3.4.1` `Actor.open_key_value_store(name=...)` and key-value-store `list_keys`, `get_value`, and `set_value` methods. See the [Python SDK storage documentation](https://docs.apify.com/sdk/python/docs/3.4/concepts/storages).

# Actor input Schema

## `snapshotStoreName` (type: `string`):

Named Apify key-value store in the account running this Actor. Reuse this exact name on later runs. Use a separate private store for each customer or SKU list.

## `skuItems` (type: `array`):

Customer SKU labels paired with already-classified 8- or 10-digit HTS codes. This Actor does not classify goods or recommend codes.

## `include9903` (type: `boolean`):

Compare structured 9903 heading rows only for the codes you submit. Independent QA passed 60 of 60 heading cases. U.S. Note body text and PDF extraction are excluded.

## Actor input object example

```json
{
  "snapshotStoreName": "hts-revision-watch-demo",
  "skuItems": [
    {
      "sku": "DEMO-8DIGIT",
      "htsCode": "0101.21.00"
    },
    {
      "sku": "DEMO-10DIGIT",
      "htsCode": "0101.21.00.10"
    }
  ],
  "include9903": true
}
```

# Actor output Schema

## `records` (type: `string`):

Open the default dataset to view the Actor's complete per-run output, including current rates, changes, unscored items, and source status.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("althorlabs/hts-revision-diff").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("althorlabs/hts-revision-diff").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call althorlabs/hts-revision-diff --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,althorlabs/hts-revision-diff"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/fVFWv6sMjsfcqG0E9/builds/A8U7cdKkeaMzyqets/openapi.json
