# Calculate duties, taxes and landed cost (POST /v1/calculate)

> POST /v1/calculate: Calculate duties, taxes and landed cost. border.bot API reference (Landed cost).

Source: https://border.bot/docs/api/calculate-landed-cost

> Documentation index: https://border.bot/llms.txt. Every page is Markdown at its URL + `.md`.

`POST https://api.border.bot/v1/calculate`

Requires an API key (`Authorization: Bearer bb_live_…`).

Duties, taxes, fees and the landed cost for one HS code / origin / destination (the destinations `GET /countries` marks `landedCost`). Costs 1 credit. Unsupported destinations return 422 `unsupported_country` without charging.

API key scope: `calculate`.

## Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `idempotency-key` | string | no | Make retries safe: a repeat with the same key returns the original result without charging again (keys are scoped to your workspace). |

## Request body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `destinationCountry` | string | yes | Destination: one `GET /countries` marks `landedCost`. ISO 3166-1 alpha-2, case-insensitive. |
| `currency` | string | no | ISO 4217 currency of `value`, `shippingCost` and `insuranceCost`. |
| `shippingCost` | number | no | Shipping cost (affects CIF-based duty/VAT bases). |
| `insuranceCost` | number | no | Insurance cost. |
| `transportMode` | "air" \| "sea" \| "road" \| "rail" | no | Transport mode (fees such as HMF apply to sea freight). |
| `shippingTerms` | "EXW" \| "FCA" \| "FAS" \| "FOB" \| "CFR" \| "CIF" \| "CPT" \| "CIP" \| "DAP" \| "DPU" \| "DDP" | no | Incoterm of the price (Incoterms 2020). Under C and D terms (CFR, CIF, CPT, CIP, DAP, DPU, DDP) the price already carries the goods to the destination, so `shippingCost` isn’t added to the customs value again. |
| `shipmentChannel` | "courier" \| "postal" | no | How the parcel travels: `courier` (express carriers, default) or `postal` — affects de minimis and channel-specific regimes. |
| `entryDate` | string | no | Calculate with the rates in force on this date (YYYY-MM-DD). Default: today. |
| `tradeAgreement` | string | no | Ignored. Preferential rates are applied automatically from the origin and destination; the agreement used is returned as `tradeAgreement`. |
| `region` | string | no | State, province or territory, where import taxes differ inside the country. Canada needs one (`ON`, `QC`, `BC`…). |
| `purpose` | "sale" \| "gift" \| "sample" \| "return" | no | Why the goods are sent: some thresholds differ for gifts, samples and returns. Default: `sale`. |
| `businessBuyer` | boolean | no | The buyer is a business: some taxes are reverse-charged or apply differently. |
| `sellerRegistrations` | string[] | no | Tax schemes the seller is registered for (`EU_IOSS`, `GB_VAT`, `AU_GST`, `NZ_GST`, `NO_VOEC`…). Taxes the seller collects at checkout are returned with `collectedBy: "seller"`. |
| `preference` | string | no | `best` (default): the lowest preferential rate the origin qualifies for. `none`: the general rate only. Or a programme code (`S` for USMCA into the US). Every option is returned in `options`. |
| `claims` | string[] | no | Exemption, relief and quota codes being claimed (US Chapter 99 exclusions such as `9903.88.69`; an end-use authorisation such as TARIC document `N990`; a tariff quota order number such as `050331`). Codes you could claim are returned in `claims.available`. |
| `enforceValidation` | boolean | no | Refuse a shipment that matches one of the destination’s `reject` validation rules (400 `invalid_input`, `details.reason: "validation_failed"`, refunded) instead of reporting it in `validation`. |
| `hsCode` | string | yes | HS/HTS code (4–10 digits; dots and spaces are ignored). |
| `originCountry` | string | yes | Country of origin — drives preferential rates and additional duties (e.g. Section 301). ISO 3166-1 alpha-2, case-insensitive. |
| `value` | number | yes | Total customs value of the goods in the shipment (unit price × quantity), in `currency`, excluding shipping. |
| `quantity` | integer | no | Number of units. |
| `weight` | number | no | Shipment weight (needed for weight-based duties). Requires `weightUnit`. |
| `weightUnit` | "kg" \| "lb" | no | Unit of `weight`. |
| `volumeLiters` | number | no | Volume of the goods in litres, for duties charged per litre (wine, spirits, fuel). |
| `alcoholPercent` | number | no | Alcohol by volume (%), for duties charged per litre of pure alcohol. |
| `components` | object[] | no | Metal content by value (line totals in `currency`), for duties charged on it: US Section 232 steel, aluminum and copper derivatives. Without it, those duties come back as `regulatory` (conditional). |
| `metalWeightPercent` | number | no | Share of the product’s weight that is metal (0–100), for content-based exemptions. |
| `conditions` | string[] | no | Rate conditions the goods meet, when the destination reserves a rate for them: a harmonised kind (`pharmaceutical`, `end_use`, `certificate`, `company`, `quality`, `route`) or the publisher’s own code (EU TARIC additional code `2500`). Without it the default rate is charged (the unconditional one, or the highest when every rate has a condition); the rates you could claim are returned in `options` with their `condition`. The importer must hold what justifies a claimed condition. |

## Responses

- `200`: The landed-cost breakdown and the credits charged. (`CalculateResponse`)
- `400`: Invalid input (`invalid_input`). (`Error`)
- `401`: Missing, invalid, revoked or expired API key (`unauthorized`). (`Error`)
- `402`: Not enough credits (`insufficient_credits`). Nothing was charged. (`Error`)
- `403`: Workspace suspended, action disabled, or the key lacks the scope (`org_suspended`, `action_disabled`, `forbidden`). (`Error`)
- `409`: A request with this Idempotency-Key is still in progress (`conflict`). (`Error`)
- `422`: Destination not supported (`unsupported_country`). (`Error`)
- `429`: Rate limited (`rate_limited`). Retry after `Retry-After` seconds; `RateLimit` says which limit was hit (`r=0`, `t` seconds until its window ends) and `RateLimit-Policy` its quota. (`Error`)
- `500`: Unexpected error (`internal`). (`Error`)
- `502`: The engine failed (`upstream_error`). The credits are refunded automatically. (`Error`)
- `504`: The engine timed out (`upstream_timeout`). The credits are refunded automatically. (`Error`)

## Example

```bash
curl -X POST 'https://api.border.bot/v1/calculate' \
  -H 'Authorization: Bearer bb_live_...' \
  -H 'Content-Type: application/json' \
  -d '{"hsCode":"6109.10.00.12","originCountry":"CN","destinationCountry":"US","value":120,"currency":"USD","shippingCost":15}'
```
