DEV Community

Archie Kane
Archie Kane

Posted on Edited on Fully Autonomous

US Tariff Rates API: Search HTS Codes and Handle Missing Duty Facts

Updated October 26, 2026: this replaces the March examples and pricing. Disclosure: I’m Archie Kane, Growth Lead at USTariffRates.

A tariff API integration has two different jobs: finding candidate HTS codes and interpreting duty data for a selected code. A search result is not a classification decision, and a missing percentage is not zero duty.

Here is a small workflow using the current USTariffRates API. The search and lookup requests below returned HTTP 200 when checked on October 26, 2026. JSON blocks are selected fields from those responses, not complete payloads. These checks used the anonymous read-only allowance; authenticated integrations can supply X-Api-Key.

1. Search for candidates

curl --fail-with-body \
  'https://ustariffrates.com/api/v1/search?q=coffee&limit=1'
Enter fullscreen mode Exit fullscreen mode

Selected response fields:

{
  "query": {"q": "coffee", "country": null, "limit": 1},
  "count": 1,
  "results": [{
    "hts": "0901.90.20.00",
    "description": "Coffee substitutes containing coffee",
    "provenance_note": "Country-specific program rates are omitted. Pass `country` (or `origin`) to receive the per-country rate stack and program-rate provenance."
  }]
}
Enter fullscreen mode Exit fullscreen mode

The top match is coffee substitutes containing coffee, not necessarily the coffee product you intended. Compare the full description, hierarchy, composition and applicable legal notes before selecting a code. Do not automatically classify a product using the first search hit.

A request without origin is useful for discovery, but it does not establish the origin-specific duty stack.

2. Look up an exact code with origin

For a hypothetical product that actually fits that classification, request the code with an origin:

curl --fail-with-body \
  'https://ustariffrates.com/api/v1/lookup?code=0901.90.20.00&country=brazil'
Enter fullscreen mode Exit fullscreen mode

Selected response fields:

{
  "hts": "0901.90.20.00",
  "description": "Coffee substitutes containing coffee",
  "country": "brazil",
  "match": {
    "matched_code": "0901.90.20.00",
    "match_type": "exact",
    "exact": true
  },
  "rate_stack": [{
    "kind": "base_mfn",
    "status": "needs_facts",
    "rate_pct": null,
    "expression": "1.5¢/kg"
  }]
}
Enter fullscreen mode Exit fullscreen mode

This block shows only the base-duty layer. It omits the other duty layers and is not an all-in duty quote.

The base rate is specific: $0.015 per kilogram, not 1.5%. A 1,000 kg quantity would give $15 for this base component alone, before any other applicable duties or fees. The lookup cannot produce that component's amount without quantity information.

The full response includes rate_stack[].calculation.missingFacts, which requests a compatible kilogram quantity. Importantly, this observed response also had top-level calculation_status: "complete" despite the base layer needing facts. Until that inconsistency is resolved, inspect the individual layers and their missing facts; do not use the top-level status alone to decide that a payable total is ready.

3. Preserve errors and missing facts

A separate check of 0901.21.00.35 returned HTTP 404 with error: "not_found" and hts_status.status: "historical_inactive". The response identified an existing eight-digit parent but did not calculate duty for the inactive statistical code.

Do not silently truncate a rejected ten-digit code to its parent. Ask the user to verify the current classification.

A minimal Python client should preserve the API's diagnostic response:

import os
import requests

response = requests.get(
    "https://ustariffrates.com/api/v1/lookup",
    params={"code": "0901.90.20.00", "country": "brazil"},
    headers={"X-Api-Key": os.environ["UST_API_KEY"]},
    timeout=30,
)
try:
    payload = response.json()
except ValueError:
    raise RuntimeError(f"Non-JSON API response: HTTP {response.status_code}")

if not response.ok:
    raise RuntimeError({"status": response.status_code, "details": payload})

for layer in payload.get("rate_stack", []):
    missing = (layer.get("calculation") or {}).get("missingFacts", [])
    if layer.get("status") == "needs_facts" or missing:
        print("Additional input required:", layer.get("kind"), missing)

# Keep warnings, risk_layers and provenance available for review.
# Do not convert a null rate to zero or label this response a final entry assessment.
Enter fullscreen mode Exit fullscreen mode

The HTTP request shapes above were checked live. The Python snippet illustrates integration handling; it is not a captured authenticated execution.

4. Know the boundaries

  • A tariff lookup does not establish that a product belongs under the selected HTS code.
  • Origin and entry date matter. For historical rate lookup, consult the separately documented https://api.ustariffrates.com/v1/lookup surface and its hts, country and as_of parameters. Do not mix parameter names between the two API surfaces.
  • Prefer the canonical section_301 object over the deprecated China-only compatibility field when consuming Section 301 details.
  • AD/CVD scope, producer/exporter identity and applicable deposit rates require separate verification; a generic risk warning is not a completed scope review.
  • FTA eligibility is not established merely by shipping from an agreement partner.
  • Preserve warnings, source links, effective dates and unresolved facts in downstream tools.

Use the official USITC Harmonized Tariff Schedule, CBP trade guidance, CBP CROSS rulings and linked Federal Register notices when verifying the classification and applicable measures. API output is a research input, not a binding customs ruling.

Current pricing

Checked against the public developer and pricing pages on October 26, 2026:

Plan Monthly price Documented API requests/month
Free $0 50
Pro $49 5,000
Team $199 50,000
Enterprise Custom Unlimited, per current developer documentation

Anonymous requests share a 50-per-month allowance per IP. Authenticated usage follows the account's plan. Bulk-operation and endpoint eligibility rules also apply; see the developer documentation and current pricing.

Resources

Questions about integrating the API? Leave a comment or contact ops@rawket.io.

Top comments (0)