API Documentation

Complete guide to integrating with the Duties & Tariffs API V.1.0.2 View changelog

Manage API Keys

Getting Started

The API provides programmatic access to comprehensive HTS (Harmonized Tariff Schedule) data through our Resolve Tariff endpoint - the primary endpoint for getting complete tariff information including base duty rates, additional measures (Section 301, 232, IEEPA), and country-specific applied rates. Also includes access to curated trade news. This RESTful API uses standard HTTP response codes and returns JSON-encoded responses.

Base URL: https://tariffsapi.com/api/v1
Quick Start: Use /api/v1/tariffs/resolve with hts and origin parameters to get complete tariff information. Set resolve_chapter_99=true to resolve reference-only measures to numeric rates.

Prerequisites

  • An active premium subscription
  • A valid API key (generate one from your API Keys page)
  • Basic knowledge of RESTful APIs and HTTP requests

Authentication

All API requests must be authenticated using your API key. We support two authentication methods:

Method 1: Bearer Token (Recommended)

Include your API key in the Authorization header:

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

Method 2: X-API-Key Header

Alternatively, use the 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 Warning: Never expose your API keys in client-side code, public repositories, or version control systems. Treat them like passwords.

Rate Limits

To ensure fair usage and system stability, API requests are rate-limited per API key:

Plan Requests per Minute Requests per Day
Premium 60 1,000

Rate Limit Headers

Every API 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 (HTS Column 1 General, Special, and Column 2) and additional measures (Section 301, 232, IEEPA) for any HTS code and country of origin.

GET /api/v1/tariffs/resolve Main Endpoint

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 Yes HTS number (any length; inheritance applies)
origin Yes ISO-2 country code (e.g., CN, MX, CA). Required to resolve additional duties.
as_of Optional Date (YYYY-MM-DD) for resolution (default: today)
include_unknown_effective_dates Optional Include measures with unknown effective dates (default: true — current unless proven otherwise). Set false to exclude.
resolve_chapter_99 Optional When true, resolve reference-only measures (e.g. "See 9903.91.05") to numeric rates by looking up the Chapter 99 HTS line in our database (default: false). Use true when the origin is subject to additional duties (e.g. China CN for Section 301) to get resolved_additional_ad_valorem_rate and total_resolved_ad_valorem_rate in the summary. Accepts true or 1. Unresolved states are returned explicitly; we never invent 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

With resolve_chapter_99=true and origin CN, reference measures (e.g. "See 9903.91.05") are resolved to numeric rates when the Chapter 99 line exists in our database:

{
  "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 New High Performance

Efficient bulk resolution. Resolve up to 200 HTS codes in a single API call with automatic deduplication and bulk data loading. 10-50x faster than individual requests. Perfect for bulk imports, rate analysis, and high-volume applications.

Performance: Process 150 requests in ~50ms with deduplication. Single API call, minimal database queries, automatic caching.

Request Body Parameters

Parameter Required Description
requests Yes Array of request objects (max 200 items)
requests[].hts Yes HTS number for this item
requests[].origin Yes 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"
      }
    }
  ]
}
Key Features:
  • Automatic deduplication - Identical requests (same HTS+origin+date) resolved once
  • Per-item errors - Some items can fail without breaking the batch
  • Cache integration - Results cached for 24 hours, same as single endpoint
  • High throughput - Process 150 requests in ~50ms (4,500+ req/s)
  • Same logic - Identical results to single /resolve endpoint
Limits: Maximum 200 items per batch. Split larger datasets into multiple requests. Each batch counts as one API request for rate limiting purposes.

Common Use Cases

Bulk Import Analysis

Analyze tariffs for entire product catalog in seconds

Multiple Origins

Compare duties for same HTS from different countries

Historical Analysis

Track rate changes over time with per-item dates

Quote Generation

Generate accurate duty calculations for multiple 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 data point. Automatically inherits from parent HTS if no duties found for exact match. 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 effective additional duties (default: today)
include_unknown_effective_dates boolean Include additional duties with unknown effective dates (default: true — current unless proven otherwise). Set false to exclude.

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 and tariff news and stay updated on the latest duty and tariff changes.

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)

Error Handling

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

Status 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

JavaScript (Node.js)

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();

Python

import requests

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

headers = {
    'Authorization': f'Bearer {API_KEY}'
}

def resolve_tariff():
    response = requests.get(
        f'{BASE_URL}/tariffs/resolve',
        headers=headers,
        params={
            'hts': '8541.10.00.80',
            'origin': 'CN',
            'resolve_chapter_99': True
        }
    )
    
    if response.status_code == 200:
        data = response.json()
        print(data)
    else:
        print(f'Error: {response.status_code}')
        print(response.json())

resolve_tariff()

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? We're here to assist you.

Documentation

This page contains comprehensive documentation for all API endpoints.

API Keys

Manage your API keys and monitor usage from your dashboard.

Manage Keys

Email Support

Contact us at support@tariffsapi.com

Latest News

Stay updated with the latest duty and tariff changes and API updates.

View News