API Documentation

Complete guide to integrating with the Duties & Tariffs API V.1.0.4 Changelog

Getting Started

The API provides programmatic access to comprehensive HTS (Harmonized Tariff Schedule) data. Get complete tariff information including base duty rates and additional measures. This RESTful API uses standard HTTP response codes and returns JSON-encoded responses.

Base URL
https://tariffsapi.com/api/v1

Prerequisites: Active premium subscription and a valid API key.

Authentication

All API requests require authentication using an API key.

Bearer Token (Recommended)

curl "https://tariffsapi.com/api/v1/tariffs/resolve?hts=8541.10.00.80&origin=CN" \
  -H "Authorization: Bearer YOUR_API_KEY"

Alternative: X-API-Key Header

curl "https://tariffsapi.com/api/v1/tariffs/resolve?hts=8541.10.00.80&origin=CN" \
  -H "X-API-Key: YOUR_API_KEY"

Security: Never expose your API keys in client-side code, public repositories, or version control systems.

Rate Limits

API requests are rate-limited per API key.

Premium Plan Limits
Per Minute
60 requests
Per Day
1,000 requests

Rate Limit Headers

Every response includes these headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1640995200

Tariff Resolution API

Resolve complete tariff information including base duty rates and additional measures (Section 301, 232, IEEPA) for any HTS code and country of origin.

GET /api/v1/tariffs/resolve

Primary consumer-facing endpoint. Given an HTS code and country of origin, returns the complete tariff picture: base duty (general or special via trade agreements) plus applicable additional measures (Section 301, 232, IEEPA). Uses HTS inheritance when needed. Returns a transparent breakdown—never silently sums rates. Use resolve_chapter_99=true to resolve reference-only measures (e.g. “See 9903.91.05”) to numeric rates by looking up the Chapter 99 HTS line in our database; rates are never invented.

Query Parameters

Parameter Required Description
hts Required HTS number (any length; inheritance applies)
origin Required ISO-2 country code (e.g., CN, MX, CA)
as_of Optional Date (YYYY-MM-DD) for resolution (default: today)
include_unknown_effective_dates Optional Include measures with unknown effective dates (default: true). Set false to exclude.
resolve_chapter_99 Optional Boolean (default: false). Resolves Chapter 99 references to numeric rates.

Example Request

const axios = require('axios');

// For origins where additional duties apply (e.g. CN), use resolve_chapter_99: true to get numeric rates
const response = await axios.get('https://tariffsapi.com/api/v1/tariffs/resolve', {
  headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
  params: {
    hts: '8541.10.00.80',
    origin: 'CN',
    as_of: '2026-02-05',
    include_unknown_effective_dates: true,
    resolve_chapter_99: true
  }
});

console.log(response.data);
import requests

# For origins where additional duties apply (e.g. CN), use resolve_chapter_99=True to get numeric rates
response = requests.get(
    'https://tariffsapi.com/api/v1/tariffs/resolve',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    params={
        'hts': '8541.10.00.80',
        'origin': 'CN',
        'as_of': '2026-02-05',
        'include_unknown_effective_dates': True,
        'resolve_chapter_99': True
    }
)

print(response.json())
require 'net/http'
require 'json'

# For origins where additional duties apply (e.g. CN), use resolve_chapter_99: true to get numeric rates
params = {
  hts: '8541.10.00.80',
  origin: 'CN',
  as_of: '2026-02-05',
  include_unknown_effective_dates: true,
  resolve_chapter_99: true
}
query = URI.encode_www_form(params)
uri = URI("https://tariffsapi.com/api/v1/tariffs/resolve?#{query}")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
puts JSON.parse(response.body)

Example Response

{
  "resolution": {
    "requested_hts": "8541.10.00.80",
    "resolved_hts": "8541.10.00",
    "origin": "CN",
    "as_of": "2026-02-05",
    "include_unknown_effective_dates": true,
    "resolve_chapter_99": true
  },
  "base_tariff": {
    "type": "general",
    "rate_type": "ad_valorem",
    "percentage_component": 0.0,
    "per_unit_component": null,
    "agreement": null
  },
  "additional_measures": [
    {
      "program": "section_301",
      "rate_type": "reference",
      "details": "See 9903.91.05",
      "chapter_99_code": "9903.91.05",
      "effective_date_unknown": true,
      "uncertain": true,
      "resolved_rate": {
        "rate_type": "ad_valorem",
        "percentage_component": 0.25,
        "source": "chapter_99",
        "chapter_99_hts": "9903.91.05"
      },
      "chapter_99_resolution": {
        "status": "resolved",
        "resolved_hts": "9903.91.05",
        "product_id": 12345
      }
    }
  ],
  "summary": {
    "applicable_ad_valorem_rate": 0.0,
    "resolved_additional_ad_valorem_rate": 25.0,
    "total_resolved_ad_valorem_rate": 25.0,
    "notes": [
      "Base duty is 0%",
      "Section 301: See 9903.91.05 for details"
    ]
  },
  "notice": "This duty and tariff resolution is advisory only and not legally binding. Always verify with official sources."
}

When resolve_chapter_99=true, reference measures may include resolved_rate (numeric rate from the Chapter 99 line) and chapter_99_resolution (status: resolved, not_found, no_numeric_rate, or multiple_rates). The summary may also include resolved_additional_ad_valorem_rate and total_resolved_ad_valorem_rate.

Missing hts or origin returns 422. Unknown HTS returns 404. Results are advisory only.

POST /api/v1/tariffs/resolve_batch Pro

Resolve multiple tariffs in a single request. Process up to 200 items with automatic deduplication and caching.

Request Body Parameters

Parameter Required Description
requests Required Array of request objects (max 200)
requests[].hts Required HTS number for this item
requests[].origin Required ISO-2 country code for this item
requests[].as_of Optional Date override for this specific item (YYYY-MM-DD)
as_of Optional Default date for all items (default: today)
include_unknown_effective_dates Optional Include measures with unknown dates (default: true)
resolve_chapter_99 Optional Resolve Chapter 99 references to numeric rates (default: false)

Example Request

const axios = require('axios');

const response = await axios.post(
  'https://tariffsapi.com/api/v1/tariffs/resolve_batch',
  {
    requests: [
      { hts: '8541.10.0080', origin: 'CN' },
      { hts: '7208.10.15', origin: 'CA' },
      { hts: '8703.23.00', origin: 'MX', as_of: '2025-01-15' }
    ],
    as_of: '2026-02-13',
    include_unknown_effective_dates: true,
    resolve_chapter_99: false
  },
  {
    headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
  }
);

console.log(`Processed ${response.data.meta.count} items in ${response.data.meta.ms_total}ms`);
console.log(response.data.results);
import requests

response = requests.post(
    'https://tariffsapi.com/api/v1/tariffs/resolve_batch',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    json={
        'requests': [
            {'hts': '8541.10.0080', 'origin': 'CN'},
            {'hts': '7208.10.15', 'origin': 'CA'},
            {'hts': '8703.23.00', 'origin': 'MX', 'as_of': '2025-01-15'}
        ],
        'as_of': '2026-02-13',
        'include_unknown_effective_dates': True,
        'resolve_chapter_99': False
    }
)

batch = response.json()
print(f"Processed {batch['meta']['count']} items in {batch['meta']['ms_total']}ms")
for result in batch['results']:
    if result['ok']:
        print(f"✓ {result['data']['resolution']['resolved_hts']}")
    else:
        print(f"✗ {result['error']['message']}")
require 'net/http'
require 'json'

uri = URI('https://tariffsapi.com/api/v1/tariffs/resolve_batch')
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer YOUR_API_KEY'
request.body = {
  requests: [
    { hts: '8541.10.0080', origin: 'CN' },
    { hts: '7208.10.15', origin: 'CA' },
    { hts: '8703.23.00', origin: 'MX', as_of: '2025-01-15' }
  ],
  as_of: '2026-02-13',
  include_unknown_effective_dates: true,
  resolve_chapter_99: false
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
batch = JSON.parse(response.body)
puts "Processed #{batch['meta']['count']} items in #{batch['meta']['ms_total']}ms"
batch['results'].each do |result|
  if result['ok']
    puts "✓ #{result['data']['resolution']['resolved_hts']}"
  else
    puts "✗ #{result['error']['message']}"
  end
end
curl -X POST https://tariffsapi.com/api/v1/tariffs/resolve_batch \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "requests": [
      { "hts": "8541.10.0080", "origin": "CN" },
      { "hts": "7208.10.15", "origin": "CA" },
      { "hts": "8703.23.00", "origin": "MX", "as_of": "2025-01-15" }
    ],
    "as_of": "2026-02-13",
    "include_unknown_effective_dates": true,
    "resolve_chapter_99": false
  }'

Example Response

{
  "meta": {
    "count": 3,
    "unique": 3,
    "ms_total": 45.23
  },
  "results": [
    {
      "index": 0,
      "ok": true,
      "data": {
        "resolution": {
          "requested_hts": "8541.10.0080",
          "resolved_hts": "8541.10.00.80",
          "origin": "CN",
          "as_of": "2026-02-13"
        },
        "base_tariff": { ... },
        "additional_measures": [ ... ],
        "summary": {
          "applicable_ad_valorem_rate": 25.0,
          "notes": ["Base: 0.0% (general) + Additional: 25.0% (section_301)"]
        }
      }
    },
    {
      "index": 1,
      "ok": true,
      "data": { ... }
    },
    {
      "index": 2,
      "ok": false,
      "error": {
        "code": "not_found",
        "message": "HTS code not found: 8703.23.00"
      }
    }
  ]
}

Limits: Maximum 200 items per batch. Each batch counts as one API request for rate limiting.

Common Use Cases

Bulk Import Analysis
Analyze entire product catalogs
Multiple Origins
Compare duties across countries
Historical Analysis
Track rate changes over time
Quote Generation
Calculate duties for line items

Performance Benchmarks

Batch Size Typical Time vs Individual Requests
10 items 50-150ms 10-20x faster
50 items 200-400ms 20-30x faster
100 items 400-800ms 20-40x faster
200 items 800-1500ms 25-50x faster

Each result includes an index field matching the original request array position and an ok boolean indicating success. Failed items include an error object with code and message fields.

GET /api/v1/tariffs/by_hts/:hts_number

Get base duties and additional measures for a single HTS code. Returns detailed duty information including all rate types.

Query Parameters

Parameter Type Description
origin string ISO country code for filtering additional duties
as_of date Date for filtering additional duties (default: today)
include_unknown_effective_dates boolean Include duties with unknown dates (default: true)

Example Request

const axios = require('axios');

const htsNumber = '8448.51.10.00';
const response = await axios.get(
  `https://tariffsapi.com/api/v1/tariffs/by_hts/${htsNumber}`,
  {
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY'
    },
    params: {
      origin: 'CN',
      include_unknown_effective_dates: true
    }
  }
);

console.log(response.data);
import requests

hts_number = '8448.51.10.00'
response = requests.get(
    f'https://tariffsapi.com/api/v1/tariffs/by_hts/{hts_number}',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    params={
        'origin': 'CN',
        'include_unknown_effective_dates': True
    }
)

print(response.json())
require 'net/http'
require 'json'

hts_number = '8448.51.10.00'
params = {
  origin: 'CN',
  include_unknown_effective_dates: true
}
query_string = URI.encode_www_form(params)
uri = URI("https://tariffsapi.com/api/v1/tariffs/by_hts/#{hts_number}?#{query_string}")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Note: The response includes detailed information about base duties, additional measures, and provides full transparency about data sources and inference confidence levels.

Newsletters API

Access curated trade newsletters and keyword-specific content.

GET /api/v1/newsletters

List all newsletters with pagination.

Example Response

{
  "newsletters": [
    {
      "id": 1,
      "title": "New Tariffs on Steel Products",
      "summary": "The U.S. announces new tariff rates...",
      "content": "Full content here...",
      "keywords": ["steel", "tariffs", "trade"],
      "published_at": "2025-01-15T10:00:00Z",
      "url": "https://tariffsapi.com/news/1"
    }
  ],
  "pagination": {
    "current_page": 1,
    "total_pages": 10,
    "total_count": 100,
    "per_page": 10
  }
}
GET /api/v1/newsletters/recent

Get the most recent newsletters (last 30 days).

Example Request

const axios = require('axios');

const response = await axios.get(
  'https://tariffsapi.com/api/v1/newsletters/recent',
  {
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY'
    }
  }
);

console.log(response.data);
import requests

response = requests.get(
    'https://tariffsapi.com/api/v1/newsletters/recent',
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
)

print(response.json())
require 'net/http'
require 'json'

uri = URI('https://tariffsapi.com/api/v1/newsletters/recent')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)
GET /api/v1/newsletters/keywords

Get all available keywords for filtering newsletters.

Example Request

const axios = require('axios');

const response = await axios.get(
  'https://tariffsapi.com/api/v1/newsletters/keywords',
  {
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY'
    }
  }
);

console.log(response.data);
import requests

response = requests.get(
    'https://tariffsapi.com/api/v1/newsletters/keywords',
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
)

print(response.json())
require 'net/http'
require 'json'

uri = URI('https://tariffsapi.com/api/v1/newsletters/keywords')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

MCP Server Integration

Pro New

Connect your AI tools directly to live tariff data using the Model Context Protocol (MCP). The MCP server exposes the same tariff resolution capabilities as the REST API, but as tool calls that AI assistants like Claude, Cursor, and other MCP-compatible clients can invoke natively.

Pro subscription required. MCP server access is available exclusively on the Professional tier ($199/month). Upgrade to Pro to get started.

Connection Details

SSE Endpoint https://tariffsapi.com/api/v1/mcp/sse
Messages Endpoint https://tariffsapi.com/api/v1/mcp/messages
Transport SSE (Server-Sent Events)
Authentication Bearer token (your API key)
Rate Limit 500 requests/minute (Pro)

Available Tools

The MCP server exposes five tools that AI assistants can call:

resolve_tariff

Resolve complete tariff information for an HTS code and origin country. Returns base tariff rates (general/special) and additional measures (Section 301, 232, IEEPA).

Parameters: hts (required), origin (required), as_of, include_unknown_effective_dates
batch_resolve_tariffs

Resolve tariffs for multiple HTS codes and origin countries in a single call (up to 200 items).

Parameters: requests (required, array), as_of, include_unknown_effective_dates
search_products

Search products by name, description, HTS code, or HS code. Useful for finding the right HTS code when you only have a product description.

Parameters: query (required), limit, has_tariffs
get_by_hts

Get detailed product and tariff information for a specific HTS code, including resolution metadata and base duties.

Parameters: hts_number (required), origin, as_of
list_agreements

List all trade agreements (USMCA, FTAs, GSP, etc.) with their codes, names, and descriptions.

Parameters: code (optional filter)

Setup Guide

Configure the MCP server in your AI client. Replace YOUR_API_KEY with a key from your API Keys dashboard.

Add to your claude_desktop_config.json file:

{
  "mcpServers": {
    "tariffs-api": {
      "url": "https://tariffsapi.com/api/v1/mcp/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
Config file location:
  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Add to your .cursor/mcp.json in the project root (or global settings):

{
  "mcpServers": {
    "tariffs-api": {
      "url": "https://tariffsapi.com/api/v1/mcp/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Add to your ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "tariffs-api": {
      "serverUrl": "https://tariffsapi.com/api/v1/mcp/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Test the MCP server with the official MCP Inspector:

npx @modelcontextprotocol/inspector \
  --url "https://tariffsapi.com/api/v1/mcp/sse" \
  --header "Authorization: Bearer YOUR_API_KEY"

Example Usage

Once connected, your AI assistant can answer tariff questions naturally. Here are some example prompts:

Prompt
"What's the tariff rate for importing solar panels (HTS 8541.10.00) from China?"
Prompt
"Compare duty rates for steel coils from Canada vs Mexico vs China."
Prompt
"Search for HTS codes related to lithium-ion batteries."
Prompt
"Which trade agreements apply to imports from Australia?"

Verify Your Connection

Test that the MCP server is reachable with a simple cURL request:

curl -N "https://tariffsapi.com/api/v1/mcp/sse" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: text/event-stream"

A successful connection returns an SSE stream. If you receive a 403 response, verify that your API key belongs to a Pro subscription account.

Troubleshooting

Error Cause Solution
401 Missing or invalid API key Check your API key in the Authorization header
403 Not on Pro tier Upgrade to Pro
429 Rate limit exceeded Wait for the Retry-After period, then retry
Connection refused Wrong endpoint URL Ensure you're using https://tariffsapi.com/api/v1/mcp/sse

Error Handling

The API uses standard HTTP status codes and returns detailed error messages in JSON format.

Code Description
200 Success - Request completed successfully
400 Bad Request - Invalid parameters
401 Unauthorized - Invalid or missing API key
404 Not Found - Resource doesn't exist
429 Too Many Requests - Rate limit exceeded
500 Server Error - Something went wrong on our end

Error Response Format

{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key provided",
    "details": "The API key is either missing, invalid, or has been revoked"
  }
}

Code Examples

Integration examples with error handling and response validation.

Integration Best Practices

Use match_type and confidence.score to determine how to display results:

  • EXACT: Show with full confidence
  • PREFIX_MATCH: Note statistical suffix applied
  • PARENT_INHERITANCE: Show warning about inherited duties
  • NOT_FOUND: Show error, check normalization.attempted_candidates

Always log request_id and check warnings[].path to identify field issues.

Integration Best Practices

Use match_type and confidence.score to determine how to display results:

  • EXACT: Show with full confidence
  • PREFIX_MATCH: Note statistical suffix applied
  • PARENT_INHERITANCE: Show warning about inherited duties
  • NOT_FOUND: Show error, check normalization.attempted_candidates

Always log request_id and check warnings[].path.

JavaScript (Node.js) - Basic Usage

const axios = require('axios');

const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://tariffsapi.com/api/v1';

async function resolveTariff() {
  try {
    const response = await axios.get(`${BASE_URL}/tariffs/resolve`, {
      headers: {
        'Authorization': `Bearer ${API_KEY}`
      },
      params: {
        hts: '8541.10.00.80',
        origin: 'CN',
        resolve_chapter_99: true
      }
    });
    console.log(response.data);
  } catch (error) {
    console.error('Error:', error.response.data);
  }
}

resolveTariff();

JavaScript - Production Integration

async function resolveTariffWithHandling(hts, origin) {
  try {
    const response = await axios.get(`${BASE_URL}/tariffs/resolve`, {
      headers: { 'Authorization': `Bearer ${API_KEY}` },
      params: { hts, origin, resolve_chapter_99: true }
    });
    
    const data = response.data;
    
    // Log request ID for debugging
    console.log(`[${data.request_id}] Resolving ${hts} from ${origin}`);
    
    // Handle match types
    const { match_type, match_level, resolved_hts_digits } = data.resolution;
    
    if (match_type === 'EXACT') {
      console.log(`✓ Exact match (${match_level} digits): ${resolved_hts_digits}`);
    } else if (match_type === 'PREFIX_MATCH') {
      console.log(`⚠ Statistical suffix applied: ${resolved_hts_digits}`);
    } else if (match_type === 'PARENT_INHERITANCE') {
      console.warn(`⚠ Inherited from parent (requested ${data.resolution.requested_level}, matched ${match_level})`);
    } else if (match_type === 'NOT_FOUND') {
      console.error(`✗ HTS not found. Attempted: ${data.normalization.attempted_candidates.map(c => c.candidate).join(', ')}`);
      return null;
    }
    
    // Check confidence and warnings
    if (data.confidence.score < 1.0) {
      console.warn(`Confidence: ${data.confidence.score} (${data.confidence.method})`);
      data.warnings.forEach(w => {
        console.warn(`  [${w.severity}] ${w.code} @ ${w.path}: ${w.message}`);
      });
    }
    
    // Verify resolved_hts_digits length matches match_level
    if (resolved_hts_digits && resolved_hts_digits.length !== match_level) {
      console.error('Schema violation: digit length mismatch');
    }
    
    return {
      rate: data.summary.applicable_ad_valorem_rate,
      measures: data.additional_measures,
      confidence: data.confidence.score,
      requestId: data.request_id
    };
    
  } catch (error) {
    console.error('API Error:', error.response?.data || error.message);
    throw error;
  }
}

Python - Production Integration

import requests
from typing import Optional, Dict, Any

API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://tariffsapi.com/api/v1'

def resolve_tariff_with_handling(hts: str, origin: str) -> Optional[Dict[str, Any]]:
    """Resolve tariff with comprehensive error handling"""
    try:
        response = requests.get(
            f'{BASE_URL}/tariffs/resolve',
            headers={'Authorization': f'Bearer {API_KEY}'},
            params={'hts': hts, 'origin': origin, 'resolve_chapter_99': True}
        )
        
        if response.status_code != 200:
            print(f"Error {response.status_code}: {response.json()}")
            return None
            
        data = response.json()
        
        # Log request ID for tracing
        print(f"[{data['request_id']}] Resolved {hts} from {origin}")
        
        # Handle match types
        match_type = data['resolution']['match_type']
        match_level = data['resolution']['match_level']
        resolved_digits = data['resolution']['resolved_hts_digits']
        
        if match_type == 'EXACT':
            print(f"✓ Exact match ({match_level} digits): {resolved_digits}")
        elif match_type == 'PREFIX_MATCH':
            print(f"⚠ Statistical suffix applied: {resolved_digits}")
        elif match_type == 'PARENT_INHERITANCE':
            requested = data['resolution']['requested_level']
            print(f"⚠ Inherited from parent (requested {requested}, matched {match_level})")
        elif match_type == 'NOT_FOUND':
            candidates = data['normalization']['attempted_candidates']
            print(f"✗ Not found. Tried: {[c['candidate'] for c in candidates]}")
            return None
        
        # Verify length guarantee
        if resolved_digits and len(resolved_digits) != match_level:
            print(f"⚠ Schema violation: digit length mismatch")
        
        # Process warnings with path context
        for warning in data['warnings']:
            print(f"[{warning['severity']}] {warning['code']} @ {warning['path']}: {warning['message']}")
        
        # Check confidence
        confidence = data['confidence']
        if confidence['score'] < 1.0:
            print(f"Confidence: {confidence['score']} ({confidence['method']})")
            for factor in confidence['factors']:
                print(f"  {factor['factor']}: {factor['score']} - {factor['reason']}")
        
        return {
            'rate': data['summary']['applicable_ad_valorem_rate'],
            'confidence': confidence['score'],
            'request_id': data['request_id']
        }
        
    except requests.RequestException as e:
        print(f"Request failed: {e}")
        return None

# Example usage
result = resolve_tariff_with_handling('8541.10.00.80', 'CN')
if result:
    print(f"Final rate: {result['rate']}% (confidence: {result['confidence']})")

Ruby

require 'net/http'
require 'json'

API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://tariffsapi.com/api/v1'

params = {
  hts: '8541.10.00.80',
  origin: 'CN',
  resolve_chapter_99: true
}
query = URI.encode_www_form(params)
uri = URI("#{BASE_URL}/tariffs/resolve?#{query}")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{API_KEY}"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

if response.code == '200'
  data = JSON.parse(response.body)
  puts data
else
  puts "Error: #{response.code}"
  puts response.body
end

PHP

<?php

$apiKey = 'YOUR_API_KEY';
$baseUrl = 'https://tariffsapi.com/api/v1';

$params = http_build_query([
    'hts' => '8541.10.00.80',
    'origin' => 'CN',
    'resolve_chapter_99' => true
]);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseUrl . '/tariffs/resolve?' . $params);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $apiKey
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode === 200) {
    $data = json_decode($response, true);
    print_r($data);
} else {
    echo "Error: $httpCode\n";
    echo $response;
}

?>

Support

Need help with the API?

Documentation

This page contains comprehensive documentation for all API endpoints.

API Keys

Manage your API keys and monitor usage.

Manage Keys

Email Support

support@tariffsapi.com

Latest News

Stay updated with the latest duty and tariff changes.

View News

Response Fields Reference

Request Tracking

request_id string

Unique identifier for tracing (format: req_[16 hex]).

as_of date

Effective date for tariff resolution (ISO 8601).

Resolution Metadata

resolution.requested_hts string

Original HTS code as provided in the request (preserves formatting).

resolution.resolved_hts string

Matched HTS code in dot format (e.g., "8541.10.00"). Primary display field.

resolution.resolved_hts_digits string

Matched HTS in digits-only format (e.g., "854110"). Use for database lookups and integrations.

Length always equals match_level. null if not found.

resolution.match_type enum

How the HTS match occurred (strict enum for machine parsing):

Value Meaning Confidence
"EXACT" Perfect match at requested specificity 1.0
"PREFIX_MATCH" Match after applying US .00 statistical suffix (8→10 digits) 0.95
"PARENT_INHERITANCE" Parent HTS fallback (duty inheritance from less specific code) 0.8
"NOT_FOUND" No match at any level (4/6/8/10 digits attempted) 0.0
resolution.match_level integer

Specificity level of matched HTS (4, 6, 8, or 10 digits). null if not found.

resolution.requested_level integer

Specificity level of requested HTS. Compare with match_level to detect inheritance.

resolution.origin string

ISO-2 country code (e.g., "CN", "MX").

resolution.as_of date

Effective date for tariff resolution (ISO 8601 format).

Base Tariff

base_tariff.type enum

Tariff type: "general" (MFN), "special" (FTA), or null (not found).

base_tariff.rate_type enum

Rate structure: "ad_valorem", "specific", or "compound".

base_tariff.percentage_component float

Ad valorem rate as decimal (e.g., 0.025 = 2.5%). null for purely specific duties.

base_tariff.per_unit_component float

Specific duty amount per unit (e.g., 0.52 USD/kg). null for ad valorem only.

base_tariff.unit_type string

Unit for specific duties (e.g., "kg", "liter", "m2").

base_tariff.agreement object

Trade agreement info if special rate applies: { code, name }. Examples: "S" (USMCA), "AU" (Australia FTA).

Additional Measures

additional_measures[] array

Additional duty measures (Section 301, 232, IEEPA) applicable to this HTS and origin.

.program enum

Program: "section_301", "section_232", "ieepa", "adcvd".

.rate_type enum

Rate structure: "ad_valorem", "specific", "compound", "reference", or "restriction".

.chapter_99_code string

Chapter 99 code for this measure (e.g., "9903.88.01"). Use with resolve_chapter_99=true to get numeric rate.

.effective_date_unknown boolean

true if effective date is uncertain (impacts confidence score).

Measures Application

measures_application enum

How multiple measures interact:

  • "STACKING" - Measures apply cumulatively (e.g., Section 301 + 232)
  • "ALTERNATIVE" - Measures are mutually exclusive options
  • "UNKNOWN" - Semantics unclear (triggers warning)

Warnings

warnings[] array

Array of warnings about data quality, match type, or resolution issues.

.code enum

Machine-readable warning code:

  • "HTS_INHERITED" - Used parent HTS (not exact match)
  • "HTS_PREFIX_MATCH" - Applied .00 suffix per US convention
  • "MEASURE_SEMANTICS_UNKNOWN" - Unclear how measures combine
  • "EFFECTIVE_DATE_UNKNOWN" - Some measures have unknown dates
.severity enum

"info", "warning", or "error".

.path string

JSON path to the related response field (e.g., "resolution.match_type").

.details object

Context-specific details (e.g., requested_level, matched_level).

Confidence Scoring

confidence.score float

Overall confidence (0.0 to 1.0). Calculated by multiplying factor scores.

score = hts_match × measure_semantics × data_completeness
confidence.method string

Method used to calculate confidence: "multiplicative" (factors are multiplied together).

confidence.factors[] array

Breakdown of confidence factors:

  • hts_match: 1.0 (exact), 0.95 (with suffix), 0.8 (inherited), 0.0 (not found)
  • measure_semantics: 1.0 (clear), 0.7 (unknown)
  • data_completeness: 1.0 (complete), 0.95 (some dates unknown)

Normalization

normalization.input string

Original HTS input (mirrors requested_hts).

normalization.normalized string

Normalized digits-only version (dots/spaces/dashes removed).

normalization.attempted_candidates[] array

All HTS codes attempted during resolution. Each entry: { candidate, level, matched }.

Useful for debugging why a match succeeded or failed.

Summary

summary.applicable_ad_valorem_rate float

Total ad valorem rate as percentage (e.g., 25.0 = 25%). Includes base + additional ad valorem measures.

summary.notes[] array

Human-readable breakdown of tariff components (base duty, Section 301, etc.).

summary.resolved_additional_ad_valorem_rate optional

Only present when resolve_chapter_99=true. Sum of resolved Chapter 99 ad valorem rates.

summary.total_resolved_ad_valorem_rate optional

Only present when resolve_chapter_99=true. Total: base + resolved additional measures.

Example Scenarios

Exact Match (Confidence: 1.0)
{
  "request_id": "req_a1b2c3d4e5f6g7h8",
  "as_of": "2026-02-19",
  "resolution": {
    "match_type": "EXACT",
    "match_level": 10,
    "requested_level": 10,
    "resolved_hts_digits": "8541100000"
  },
  "confidence": {
    "score": 1.0,
    "method": "multiplicative",
    "factors": [
      { "factor": "hts_match", "score": 1.0, "reason": "Perfect match" }
    ]
  },
  "warnings": []
}
Inherited Match (Confidence: ~0.76)
{
  "request_id": "req_b2c3d4e5f6g7h8i9",
  "as_of": "2026-02-19",
  "resolution": {
    "requested_hts": "8541.10.00.80",
    "resolved_hts": "8541.10.00",
    "resolved_hts_digits": "854110",
    "match_type": "PARENT_INHERITANCE",
    "match_level": 6,
    "requested_level": 10
  },
  "confidence": {
    "score": 0.76,
    "method": "multiplicative",
    "factors": [
      { "factor": "hts_match", "score": 0.8, "reason": "Inherited from parent" },
      { "factor": "measure_semantics", "score": 1.0 },
      { "factor": "data_completeness", "score": 0.95 }
    ]
  },
  "warnings": [
    {
      "code": "HTS_INHERITED",
      "severity": "warning",
      "path": "resolution.match_type",
      "details": { "requested_level": 10, "matched_level": 6 }
    }
  ]
}
Not Found (Confidence: 0.0)
{
  "request_id": "req_c3d4e5f6g7h8i9j0",
  "as_of": "2026-02-19",
  "resolution": {
    "match_type": "NOT_FOUND",
    "match_level": null,
    "requested_level": 10,
    "resolved_hts_digits": null
  },
  "base_tariff": { "type": null },
  "confidence": {
    "score": 0.0,
    "method": "multiplicative"
  },
  "warnings": [
    {
      "code": "HTS_NOT_FOUND",
      "severity": "error",
      "path": "resolution.match_type"
    }
  ],
  "normalization": {
    "attempted_candidates": [
      { "candidate": "9999999999", "level": 10, "matched": false },
      { "candidate": "99999999", "level": 8, "matched": false },
      { "candidate": "999999", "level": 6, "matched": false },
      { "candidate": "9999", "level": 4, "matched": false }
    ]
  }
}

Batch Resolution Fields

meta.request_id string

Batch-level request ID (format: batch_[16 hex]).

results[].request_id string

Per-item request ID for tracing individual results within the batch.

results[].ok boolean

true if resolution succeeded, false if error occurred.

results[].error.details object

When ok=false, includes requested_hts and attempted_candidates for debugging.