阅读 API 契约
下方契约涵盖请求模式、输入输出结构、错误语义与限流规则。先花二十分钟阅读,可省下数天的调试时间。
API 参考
将 HTS 归类与关税计算直接集成到您的系统。发送产品描述和原产国,一次 REST 调用即可获取 HTS 编码与完整的关税叠加明细。
性能
数百? 上千? Ginger 在几分钟内帮你全部搞定。
一次 API 调用即可归类单个产品。
以下为生产环境实际测量的响应时间。较低的百分位数代表多数情况的体验,较高的百分位数反映最差情况的尾部表现。
平均
36 秒
所有生产环境请求的平均响应时间。
中位数 (p50)
30 秒
半数请求可在此时间内完成。
p95
79 秒
95% 的请求可在此时间内完成,代表典型的最差情况。
p99
108 秒
即使在高负载下,99% 的请求仍可在此时间内完成。
一次 API 调用最多可归类 200 个产品。
为大型品类目录而生。单一请求并行处理整个批次,并在数分钟内完成。
每次调用笔数
200
单次批量调用可归类的最大产品数量。
完成时间
3-5 分钟
处理完整批次的典型端到端时间。
每日处理量
200K+
生产环境每日归类量。定制化企业方案可扩展至每小时 100K 次归类。
第 91 章产品(如手表)需按组成部件分别计税,而非以整体计算。API 会自动将这类产品拆解为组成部件并在响应中返回 components 数组,每个部件都有独立的 HTS 编码与关税计算。大多数归类 API 直接跳过这项能力,但真正的贸易合规意味着每一个产品都必须获得正确的关税待遇,没有例外。
GingerControl OpenAPI 是自动化 HTS 归类与关税计算的 REST 接口。发送产品描述和原产国,一次调用即可获取 HTS 编码与完整的关税明细。
归类结果定位为您团队的研究产出:在我们 1,000+ 个产品的内部基准测试中,引擎达到 99.86% 准确率,且每条结果都设计为交由持牌报关行复核后再申报。
单品端点
每次请求归类一个产品,附完整关税细节。
批量端点
单次请求最多 200 个产品。
拆分码支持
拆分码产品提供部件级关税计算。
完整关税叠加
General (MFN) 与 Special 税率、Section 301、232、122,以及所有适用的第 99 章条目。
下方契约涵盖请求模式、输入输出结构、错误语义与限流规则。先花二十分钟阅读,可省下数天的调试时间。
与我们联系,我们会通过 email 等线下渠道发放测试 Key 给您。
注意
测试 Key 流量配额较小,有效期通常仅 7-30 天,仅供开发与调试使用,不得用于生产环境。
多数团队能快速完成基本对接,真正花时间的是把返回数据集成进自己的系统。
准备上线时,请与我们联系申请生产 Key。我们会依据您的调用模式、IP 白名单与峰值 QPS 配置额度。
注意
请妥善保管生产 API Key。现阶段 API Key 是计费的唯一依据。
Method: POST
Path: /openapi/v1/tariff
Base URL: https://api.gingercontrol.com
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
X-Api-Key | Yes | Caller credential issued offline by GingerControl. Used for authentication, rate limiting, and quota control. |
X-Request-Id | No | Request trace identifier for troubleshooting and log tracing. Server generates one if omitted. |
| Header | Description | Purpose |
|---|---|---|
Content-Type | Always application/json | Indicates the response body format |
X-Request-Id | Echoes the caller-provided value, or a server-generated value | Recommended for logging and troubleshooting |
Retry-After | Returned only with 429 Too Many Requests | Tells the caller how many seconds to wait before retrying |
| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | Product description. Must not be empty; max 10,000 characters. |
country_of_origin | string | Yes | ISO 3166-1 alpha-2 country code. EU (European Union) and UK (United Kingdom) are also accepted; GB is accepted and processed as UK. |
extra | object | No | Additional input. The current version only consumes steel_pour_country and aluminum_pour_country; all other fields are ignored. |
extra.steel_pour_country | string | No | Steel pour country. Same rules as country_of_origin. |
extra.aluminum_pour_country | string | No | Aluminum pour country. Same rules as country_of_origin. |
Request Example
{
"description": "Cotton knit short sleeve T-shirt",
"country_of_origin": "DE",
"extra": {
"steel_pour_country": "IT",
"aluminum_pour_country": "FR"
}
}Response Example
{
"hts_code": "6109.10.0012",
"tariffs": {
"general_rate": "16.5%",
"special_rate": "Free",
"Section 301": [],
"Section 232 - Metals": [],
"Section 122": [
{
"code": "9903.03.01",
"rate": "10%"
}
]
}
}| Field | Type | Meaning | Notes |
|---|---|---|---|
hts_code | string | The HTS code determined for the product | Normally a 10-digit code; a split-code product may return an 8-digit parent code such as 9101.11.40 |
tariffs | object | null | The set of tariff-related data for the product | May be null for split-code products |
tariffs.general_rate | string | null | General rate | This may also be understood as MFN |
tariffs.special_rate | string | null | Special rate | The raw text is lightly normalized, for example by removing a trailing parenthetical note |
tariffs.<module_key> | array | List of Chapter 99 entries for the module | Each element is {code, rate} |
tariffs.<module_key>[].code | string | Chapter 99 code | For example, 9903.85.08 |
tariffs.<module_key>[].rate | string | Rate | Percentage string such as 25% |
components | array | Component list for a split-code product | Omitted for ordinary products |
components[].hts_code | string | Component HTS code | Always 10 digits |
components[].tariffs | object | null | Tariff data for the component |
| Field | Type | Meaning | Notes |
|---|---|---|---|
detail | object | Error object | |
detail.code | string | Error code | Intended for programmatic handling |
detail.message | string | Error message | Intended for logging and troubleshooting |
Error Response Example
{
"detail": {
"code": "invalid_request",
"message": "Invalid request body."
}
}| HTTP Status | Code | Description |
|---|---|---|
401 | missing_api_key | X-Api-Key was not provided |
401 | invalid_api_key | API key validation failed |
403 | api_key_revoked | The API key has been revoked |
403 | client_disabled | The caller account has been disabled |
422 | invalid_request | Request body is malformed or fields do not satisfy the contract |
429 | request_rate_limited | Request-level protection was triggered |
429 | item_rate_limited | Item-level quota protection was triggered |
500 | classification_failed | HTS identification failed |
500 | calculator_failed | Tariff calculation failed |
500 | internal_error | An unclassified internal error occurred |
Replace YOUR_API_KEY with the key issued to you, then run:
curl -X POST 'https://api.gingercontrol.com/openapi/v1/tariff' \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'X-Request-Id: manual-single-001' \
-d '{
"description": "Cotton knit short sleeve T-shirt",
"country_of_origin": "DE",
"extra": {
"steel_pour_country": "IT",
"aluminum_pour_country": "FR"
}
}'Method: POST
Path: /openapi/v1/tariff/batch
Base URL: https://api.gingercontrol.com
Request and response headers are identical to the Single-Product Endpoint: Content-Type, X-Api-Key, and optional X-Request-Id on the request; X-Request-Id and Retry-After (on 429) on the response.
| Field | Type | Required | Description |
|---|---|---|---|
items | array | Yes | List of products. Up to 200 items per request. |
items[].item_id | string | Yes | Caller-defined unique identifier for result reconciliation. Must be unique within the request. |
items[].description | string | Yes | Product description. Must not be empty; max 10,000 characters. |
items[].country_of_origin | string | Yes | Same rules as the single-product country_of_origin. |
items[].extra | object | No | Same rules as the single-product extra object. |
Request Example
{
"items": [
{
"item_id": "SKU-DE-001",
"description": "Cotton knit short sleeve T-shirt",
"country_of_origin": "DE",
"extra": {
"steel_pour_country": "IT"
}
},
{
"item_id": "SKU-FR-002",
"description": "Cotton crew neck T-shirt",
"country_of_origin": "FR",
"extra": {}
}
]
}Response Example
{
"items": [
{
"item_id": "SKU-DE-001",
"status": "ok",
"hts_code": "6109.10.0012",
"tariffs": {
"general_rate": "16.5%",
"special_rate": "Free",
"Section 301": [],
"Section 232 - Metals": [],
"Section 122": [
{ "code": "9903.03.01", "rate": "10%" }
]
}
},
{
"item_id": "SKU-FR-002",
"status": "ok",
"hts_code": "6109.10.0004",
"tariffs": {
"general_rate": "16.5%",
"special_rate": "Free",
"Section 301": [],
"Section 232 - Metals": [],
"Section 122": [
{ "code": "9903.03.01", "rate": "10%" }
]
}
}
],
"summary": {
"total": 2,
"succeeded": 2,
"failed": 0
}
}| Field | Type | Meaning | Notes |
|---|---|---|---|
items | array | List of batch processing results | The response order matches the request order |
items[].item_id | string | Caller-provided unique identifier | Used for result reconciliation |
items[].status | string | Processing status | Either ok or failed |
items[].hts_code | string | HTS code for a successful item | Normally a 10-digit code; a split-code product may return an 8-digit parent code; present only when status = ok |
items[].tariffs | object | null | Tariff data for a successful item | May be null for split-code products; present only when status = ok |
items[].tariffs.general_rate | string | null | General rate | This may also be understood as MFN |
items[].tariffs.special_rate | string | null | Special rate | Lightly normalized before being returned |
items[].tariffs.<module_key> | array | Module entries | Each element is {code, rate} |
items[].tariffs.<module_key>[].code | string | Chapter 99 code | |
items[].tariffs.<module_key>[].rate | string | Rate | |
items[].components | array | Component list for a split-code product | Omitted for ordinary products; present only when status = ok |
items[].components[].hts_code | string | Component HTS code | Always 10 digits |
items[].components[].tariffs | object | null | Tariff data for the component | |
items[].code | string | Failure code for a failed item | Present only when status = failed |
summary | object | Summary information for the batch request | |
summary.total | integer | Total number of items in this request | |
summary.succeeded | integer | Number of successful items | |
summary.failed | integer | Number of failed items |
| Field | Type | Meaning | Notes |
|---|---|---|---|
detail | object | Error object | |
detail.code | string | Error code | Intended for programmatic handling |
detail.message | string | Error message | Intended for logging and troubleshooting |
Error Response Example
{
"detail": {
"code": "item_rate_limited",
"message": "Item quota exceeded."
}
}Batch-Level Error Codes
| HTTP Status | Code | Description |
|---|---|---|
401 | missing_api_key | X-Api-Key was not provided in the request headers |
401 | invalid_api_key | API key validation failed |
403 | api_key_revoked | The API key has been revoked |
403 | client_disabled | The caller account has been disabled |
422 | invalid_request | The top-level structure is invalid, item_id values are duplicated, fields are missing, field types are incorrect, or field values do not satisfy the contract |
429 | request_rate_limited | Request-level protection was triggered |
429 | item_rate_limited | Item-level quota protection was triggered |
500 | internal_error | An unclassified batch-level internal error occurred |
Item-Level Failure Codes
| Code | Description |
|---|---|
classification_failed | HTS identification failed for that item |
calculator_failed | Tariff calculation failed for that item |
internal_error | An unclassified internal error occurred for that item |
Replace YOUR_API_KEY with the key issued to you, then run:
curl -X POST 'https://api.gingercontrol.com/openapi/v1/tariff/batch' \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'X-Request-Id: manual-batch-001' \
-d '{
"items": [
{
"item_id": "SKU-DE-001",
"description": "Cotton knit short sleeve T-shirt",
"country_of_origin": "DE",
"extra": { "steel_pour_country": "IT" }
},
{
"item_id": "SKU-FR-002",
"description": "Cotton crew neck T-shirt",
"country_of_origin": "FR",
"extra": {}
}
]
}'429 Too Many Requests.Validate request bodies
If you receive 422, the request body structure, field types, or field values usually do not match the contract. Check the request body first.
Handle rate limits gracefully
If you receive 429, implement retry behavior using the Retry-After response header.
Log request IDs
Keep request logs and record the X-Request-Id for each call. This significantly reduces troubleshooting time.
Configure timeouts and retries
Configure reasonable timeouts, retries, and failure alerts so that network fluctuations can be handled robustly.
Do not embed your API key in frontend code, browser code, or any environment exposed to end users.
Do not use production API keys in test, development, or temporary debugging environments.
Store your API key in a secure configuration system, such as a secret manager or tightly controlled environment variables.
If an API key is lost, suspected to be leaked, or needs to be rotated, please contact us as soon as possible.
For integration support, testing, production rollout, or ongoing operations, please contact us:
Email: chen@gingercontrol.com
We use cookies to understand how visitors interact with our site. No personal data is shared with advertisers.