API Reference
Programmatic access to US tariff rates and HS-code
data. Base URL: https://exportsonar.com/api/v1/. Endpoints are live in
production; every example value below is derived from the current HTS
dataset.
Data vintage: 2026-10-09
· 23,193 tariff lines across 96 chapters ·
interactive OpenAPI spec at /api/docs
Authentication & quotas
| Mode | Header | Quota |
|---|---|---|
| Anonymous (free) | — | Unlimited, no signup |
| Metered (paid) | X-API-Key: <key> | Prepaid credits, $0.06 per successful call |
A metered call debits exactly one credit; the response carries
credits_remaining. Errors: 401 INVALID_API_KEY,
402 INSUFFICIENT_CREDITS.
GET /api/v1/health
Liveness probe with dataset dimensions. No parameters.
curl https://exportsonar.com/api/v1/health
# => {"ok": true, "rows": 23193, "chapters": 96}
GET /api/v1/tariff
Duty estimate for one HTS line. Query parameters:
| Param | Required | Notes |
|---|---|---|
hs | yes | dotted or bare; 8-digit base lines auto-fall-back to the 10-digit suffix |
origin | yes | ISO country of origin (upper-cased server-side) |
value | yes | customs value in USD, > 0 |
qty | no | quantity for specific duties |
unit | no | unit qualifier for qty (lower-cased server-side) |
curl "https://exportsonar.com/api/v1/tariff?hs=0101.30.00.00&origin=CN&value=1000"
# => {"query": {"hs": "0101.30.00.00", "hs_normalized": "0101.30.00.00", "origin": "CN", ...},
# "match": {"hts": "0101.30.00.00", "description": "Asses", "unit": "No."},
# "rate": {"basis": "ad_valorem", "general": "6.8%",
# "ad_valorem_pct": 6.8, "specific": null,
# "duty_usd": 68.0, "note": "..."},
# "trade_remedy": [...]} # present when Chapter 99 provisions apply
Errors: 400 MISSING_PARAM, 400 INVALID_VALUE,
404 HTS_NOT_FOUND, plus the auth errors above. When the
requested line carries an active Chapter 99 trade-remedy provision,
the response embeds a trade_remedy array with the surcharge
detail.
GET /api/v1/lookup
Prefix-first HS-code and description search. Parameters:
q (required, non-blank), limit (optional,
clamped to 1–100, default 10).
curl "https://exportsonar.com/api/v1/lookup?q=asses&limit=3"
# => {"q": "asses", "count": 3, "results": [{"hts": "...", "description": "..."}, ...]}
Error: 400 MISSING_PARAM when q is blank.
GET /api/v1/usage
Balance and recent metered calls for one key. Requires
X-API-Key; anonymous callers get
401 INVALID_API_KEY.
curl -H "X-API-Key: $EXPORTSONAR_KEY" https://exportsonar.com/api/v1/usage
# => {"ok": true, "credits_remaining": 87, "recent_calls": [...]}
Code examples — curl, Python & Node
The same duty estimate in three languages. Every example uses the
live production base URL and works with an anonymous (free) request;
add your X-API-Key header to receive metered responses
with credits_remaining.
Python (stdlib only)
import json
from urllib.parse import urlencode
from urllib.request import urlopen
BASE = "https://exportsonar.com"
params = urlencode({"hs": "0101.30.00.00", "origin": "CN", "value": "1000"})
with urlopen(f"{BASE}/api/v1/tariff?{params}") as resp:
data = json.load(resp)
rate = data["rate"]
print(rate["basis"], rate["duty_usd"])
# => percent 68.0
Node.js (18+, fetch built in)
const BASE = "https://exportsonar.com";
const params = new URLSearchParams({
hs: "0101.30.00.00", origin: "CN", value: "1000",
});
const resp = await fetch(`${BASE}/api/v1/tariff?${params}`, {
headers: { "X-API-Key": process.env.EXPORTSONAR_KEY },
});
if (!resp.ok) throw new Error(`API ${resp.status}`);
const data = await resp.json();
console.log(data.rate.basis, data.rate.duty_usd);
// => percent 68.0
curl
curl "https://exportsonar.com/api/v1/tariff?hs=0101.30.00.00&origin=CN&value=1000"
# => {"query": {"hs": "0101.30.00.00", ...}, "rate": {"duty_usd": 68.0, ...}}
# metered variant: add -H "X-API-Key: $EXPORTSONAR_KEY"
Error codes
| HTTP | Code | Meaning |
|---|---|---|
| 400 | MISSING_PARAM | a required query parameter is absent or blank |
| 400 | INVALID_VALUE | value/qty is not a finite positive number |
| 401 | INVALID_API_KEY | unknown or malformed X-API-Key |
| 402 | INSUFFICIENT_CREDITS | metered balance exhausted |
| 404 | HTS_NOT_FOUND | no HTS line matches after suffix fallback |
API FAQ
Do I need an account?
No — anonymous access is unlimited and signup-free; a key only adds metering.
What does it cost?
$0.06 per successful call against prepaid credits; anonymous calls are free.
Unknown HS code?
404 HTS_NOT_FOUND, after automatic 8→10 digit suffix fallback.
Data provenance?
Official USITC Harmonized Tariff Schedule export plus Chapter 99 remedy provisions.