Skip to content
GOODS ACROSS BORDERS

FOR DEVELOPERS

Code examples

Eight common use cases against the GAB API. Each has a curl call, a TypeScript snippet using @goodsacrossborders/sdk, and a Python equivalent using the stdlib.

1. Fetch a country profile

Get the full customs profile for one destination (allowances, de minimis, VAT, restricted/prohibited).

CURL
curl https://goodsacrossborders.com/api/v1/countries/thailand.json
TYPESCRIPT
import { createClient } from "@goodsacrossborders/sdk";
const gab = createClient();
const thailand = await gab.getCountry("thailand");
console.log(thailand.vatRateStandard, thailand.deMinimisImport);
PYTHON
import urllib.request, json
r = urllib.request.urlopen("https://goodsacrossborders.com/api/v1/countries/thailand.json")
country = json.loads(r.read())
print(country["vatRateStandard"], country["deMinimisImport"])

2. Look up a single rule (item × country × purpose)

Get a specific rule by composite key, e.g. can I bring a vape to Thailand as a traveller?

CURL
curl https://goodsacrossborders.com/api/v1/rules/vape-e-cigarette_thailand_traveling.json
TYPESCRIPT
const rule = await gab.getRule("vape-e-cigarette", "thailand", "traveling");
console.log(rule.status, rule.statusSummary);  // -> "prohibited", "Completely banned..."
PYTHON
r = urllib.request.urlopen(
  "https://goodsacrossborders.com/api/v1/rules/vape-e-cigarette_thailand_traveling.json"
)
rule = json.loads(r.read())
print(rule["status"], rule["statusSummary"])

3. Filter rules client-side

Find every restricted rule for a country, or every rule for an item across all countries.

CURL
# fetch all rules + grep
curl https://goodsacrossborders.com/api/v1/rules.json | jq '.[] | select(.countrySlug=="japan" and .status=="prohibited")'
TYPESCRIPT
const prohibitedInJapan = await gab.findRules({ countrySlug: "japan" });
const onlyProhibited = prohibitedInJapan.filter((r) => r.status === "prohibited");
PYTHON
r = urllib.request.urlopen("https://goodsacrossborders.com/api/v1/rules.json")
rules = json.loads(r.read())
prohibited = [r for r in rules if r["countrySlug"] == "japan" and r["status"] == "prohibited"]

4. Sync rule changes nightly

Watch the changelog endpoint and alert when something corrected/removed lands.

CURL
curl https://goodsacrossborders.com/api/v1/changelog.json | jq '.[] | select(.changeType=="corrected" or .changeType=="removed")'
TYPESCRIPT
const log = await gab.listChangelog();
const today = new Date().toISOString().slice(0, 10);
const todays = log.filter((e) => e.date === today);
for (const entry of todays) {
  if (entry.changeType === "corrected" || entry.changeType === "removed") {
    notifyTeam(entry.summary);  // your alert hook
  }
}
PYTHON
import datetime
r = urllib.request.urlopen("https://goodsacrossborders.com/api/v1/changelog.json")
log = json.loads(r.read())
today = datetime.date.today().isoformat()
for e in log:
  if e["date"] == today and e["changeType"] in ("corrected", "removed"):
    notify_team(e["summary"])

5. Build a duty-rate lookup

Query the bilateral duty matrix to estimate landed cost.

CURL
curl https://goodsacrossborders.com/api/v1/duty-rates.json | jq '.[] | select(.originCountrySlug=="china" and .destinationCountrySlug=="united-states" and .hsChapter=="85")'
TYPESCRIPT
const rates = await gab.listDutyRates();
const match = rates.find(
  (r) => r.originCountrySlug === "china"
       && r.destinationCountrySlug === "united-states"
       && r.hsChapter === "85",
);
if (match) console.log(`Duty: ${match.dutyRatePercent}%`);
PYTHON
r = urllib.request.urlopen("https://goodsacrossborders.com/api/v1/duty-rates.json")
rates = json.loads(r.read())
match = next((x for x in rates
  if x["originCountrySlug"] == "china"
  and x["destinationCountrySlug"] == "united-states"
  and x["hsChapter"] == "85"), None)
if match: print(f"Duty: {match['dutyRatePercent']}%")

6. Power a customs-warning widget at checkout

Given the buyer's country and cart items, surface restricted/prohibited items before payment.

CURL
# pseudocode — combine /items + /rules for each cart line
TYPESCRIPT
async function checkCart(items: string[], destinationSlug: string) {
  const flags = [] as Array<{ item: string; status: string; summary: string }>;
  for (const itemSlug of items) {
    try {
      const r = await gab.getRule(itemSlug, destinationSlug, "importing-personal");
      if (r.status !== "allowed") {
        flags.push({ item: itemSlug, status: r.status, summary: r.statusSummary });
      }
    } catch { /* no rule indexed — surface as VERIFY */ }
  }
  return flags;
}
PYTHON
def check_cart(items, destination_slug):
  flags = []
  for slug in items:
    try:
      r = urllib.request.urlopen(
        f"https://goodsacrossborders.com/api/v1/rules/{slug}_{destination_slug}_importing-personal.json"
      )
      rule = json.loads(r.read())
      if rule["status"] != "allowed":
        flags.append({"item": slug, "status": rule["status"], "summary": rule["statusSummary"]})
    except Exception:
      pass
  return flags

7. List trade lanes with an active FTA

Filter the route-pair feed to lanes where a free-trade agreement is in force.

CURL
curl https://goodsacrossborders.com/api/v1/route-pairs.json | jq '.[] | select(.ftaStatus=="in-force")'
TYPESCRIPT
const lanes = await gab.listRoutePairs();
const active = (lanes as Array<{ ftaStatus?: string; ftaName?: string }>).filter(
  (l) => l.ftaStatus === "in-force",
);
PYTHON
r = urllib.request.urlopen("https://goodsacrossborders.com/api/v1/route-pairs.json")
lanes = json.loads(r.read())
active = [l for l in lanes if l.get("ftaStatus") == "in-force"]

8. Drop into an LLM tool-use schema

Wire the SDK into a function-calling agent so the LLM can return live rules instead of hallucinating.

CURL
# n/a — agent-orchestrator integration
TYPESCRIPT
// Anthropic / OpenAI tool definition
const tools = [{
  name: "get_customs_rule",
  description: "Live customs rule for an item × country × purpose.",
  input_schema: {
    type: "object",
    properties: {
      itemSlug: { type: "string" },
      countrySlug: { type: "string" },
      purpose: { type: "string", enum: ["traveling", "posting", "importing-personal", "importing-commercial"] },
    },
    required: ["itemSlug", "countrySlug", "purpose"],
  },
}];

// when the model calls get_customs_rule:
async function handleToolCall(args) {
  const r = await gab.getRule(args.itemSlug, args.countrySlug, args.purpose);
  return { status: r.status, summary: r.statusSummary, sources: r.sourceUrls };
}
PYTHON
# Same pattern — wire your function-calling framework's tool handler to:
def handle_tool_call(args):
  r = urllib.request.urlopen(
    f"https://goodsacrossborders.com/api/v1/rules/{args['itemSlug']}_{args['countrySlug']}_{args['purpose']}.json"
  )
  rule = json.loads(r.read())
  return {"status": rule["status"], "summary": rule["statusSummary"], "sources": rule.get("sourceUrls", [])}

See also: /api reference, /developers/openapi spec, /developers/attribution.

STAY IN THE LOOP

We don't have a form backend, so these are pre-filled mailto links. Send a one-line email and we'll add you.

RULES5,339COUNTRIES85ITEMS111CARRIERS28GUIDES46UNIQUE SOURCES645LATEST VERIFICATION2026-07-26MethodologyChangelog