REST · MCP · GPT · OpenAPI 3.1.0

The reference layer, documented

24 REST endpoints · 25 MCP tools — every response carries its _source: the authority, the dataset edition, and where it came from.

> tools/call adr_lookup { "un_number": "1203" } { "count": 1, "results": [ { "verified": false, "verification": { "status": "unverified", "basis": "dataset", "reason": "No row-level audit. Transcribed from the UNECE ADR 2025 Table A and held by the dataset-level checks only (56 anchor rows carry a row-level audit)." }, "un_number": "1203", "proper_shipping_name": "MOTOR SPIRIT or GASOLINE or PETROL", "class": "3", "classification_code": "F1", "packing_group": "II", "labels": "3", "special_provisions": "243 534 664", "limited_quantity": "1 L", "excepted_quantity": "E2", "transport_category": "2", "tunnel_restriction_code": "(D/E)", "hazard_identification_number": "33", "variant_index": 0, "variant_count": 1 } ], "meta": { "source": "ADR 2025 (UNECE, ECE/TRANS/352, applicable 1 Jan 2025); given legal effect by EU Directive 2008/68/EC (consolidated). Factual compilation, best-effort — not legal advice, not a regulatory authority.", "edition": "ADR 2025", "entries": 2939 }, "_source": { "type": "reference", "authority": "UNECE", "dataset": "ADR 2025", "source_url": "https://unece.org/adr-2025-files", "data_vintage": "ADR 2025", "licence": "factual compilation, no named open licence", "retrieved_via": "freightutils.com", "dataset_edition": "ADR 2025", "authority_current_edition": "ADR 2025", "checked_at": "2026-09-12" } }
npx freightutils-mcp
Claude DesktopCursorChatGPTn8nZapierMake
Open Playground →📥 Download OpenAPI 3.1.0 Spec (JSON)📥 Download Postman Collection

Compatible with Swagger and Postman import

Overview

The FreightUtils API is the neutral freight reference layer for AI agents — source-backed dangerous-goods, customs, location and freight-calculation data an agent can call and cite, from primary sources (ADR 2025 / UNECE, HS 2022 / WCO, IATA-regulated airline prefixes). Neutral by design: no freight to sell and no carrier to push — a lookup returns what the named source says, with the citation attached so an agent can inspect it rather than take our word for it. It is a stateless REST API; every calculator on this site has a corresponding endpoint. No authentication is required. Responses are JSON. CORS is enabled for all origins.

Base URL: https://www.freightutils.com/api
Download OpenAPI 3.1.0 Spec (JSON)

Reliability & Support

FreightUtils APIs are hosted on Vercel's global edge network with automatic SSL and CDN caching.

Status
APIs are actively maintained and monitored.
Support
contact@freightutils.com — corrections and API issues typically addressed within 2 business days.
Versioning
Reference endpoints (ADR, HS, airlines) include a meta object with data source and edition information. Breaking changes will be announced via the API docs page.
Open Access Tier
No authentication required for casual use. Rate limit: 25 requests per day per IP. Free API keys available for 100/day.
Commercial Access
For production integrations with higher limits, see Pro pricing or contact us directly.
Free API Key
Get 100 requests/day — no credit card required
Enter your email and we'll send you an API key instantly.
Anonymous: 25/day · Free key: 100/day · Pro: 50,000/month
Need higher limits? See our plans →

MCP Server — AI Agent Integration

The full MCP server reference — all 25 tools, the response envelope, rate limits and directory listings — lives on its own page.

MCP server docs →

FreightUtils is the neutral freight reference layer for AI agents, available as a Model Context Protocol (MCP) server — direct, citable access to all 25 MCP tools (24 REST-backed + get_subscribe_link) from named primary sources, with no freight to sell and no carrier to push.

Install via npm
npx freightutils-mcp
Or connect via URL
https://www.freightutils.com/api/mcp
Claude Desktop Configuration
Add to your claude_desktop_config.json:
{ "mcpServers": { "freightutils": { "command": "npx", "args": ["freightutils-mcp"] } } }
First-time setup verification

After saving the config, fully quit and relaunch your MCP client (Claude Desktop, Cursor, Cline). MCP servers are only loaded at client startup — editing the config in a running session does nothing until restart.

Confirm the FreightUtils MCP surface is reachable before asking your agent any freight question:

curl https://www.freightutils.com/api/mcp/health

A 200 response with "status":"ok" and "tools_registered":25 means the remote MCP surface is live. The endpoint is callable by your agent too — Claude/Cursor/Cline can hit it for a self-diagnostic without you having to open a terminal.

Cursor Configuration
Cursor uses an mcp.json file (Settings → MCP). Same shape as Claude Desktop:
{ "mcpServers": { "freightutils": { "command": "npx", "args": ["-y", "freightutils-mcp"] } } }
Cline Configuration
Cline (VS Code extension): open the MCP server settings panel, click "Edit MCP Settings", add:
{ "mcpServers": { "freightutils": { "command": "npx", "args": ["-y", "freightutils-mcp"], "disabled": false, "autoApprove": [] } } }
Troubleshooting
SymptomLikely causeFix
Tools not appearing in the clientMCP client wasn’t restarted after the config editFully quit (Cmd+Q on macOS / right-click → Quit on Windows tray) and relaunch. Don’t just close the window.
"Server failed to start" / spawn error in client logsnpx not on PATH, or node version older than 18Install Node.js 18+ from nodejs.org. On macOS, an absolute path in the config (e.g. "/opt/homebrew/bin/npx") avoids PATH issues for GUI-launched clients.
Tool calls return HTTP 429 / "rate_limited"Anonymous IP cap of 25 requests/day exceededGet a free API key from the signup form below (100/day) or upgrade to Pro (50,000/month). The freightutils-mcp npm package passes the key through on every call (since v2.3.0) — set the FREIGHTUTILS_API_KEY env var in your MCP client config. On the remote URL (https://www.freightutils.com/api/mcp), send it as an X-API-Key header.
Specific tool returns "isError": trueBad input shape (snake_case vs camelCase, missing required field) or unknown lookup key (UN number / HS code / AWB prefix not in the dataset)The error message in the tool response names the field. Verify against the schema at /api-docs or call the corresponding playground endpoint directly to confirm the input shape.
Want to verify the surface from inside an agentNo CLI access during a conversationAsk the agent to fetch /api/mcp/health. The endpoint is public, returns the server version + registered tool count + transport URLs in a single JSON, and is rate-limit exempt so the diagnostic always works.
What AI agents can do
Calculate loading metres for 20 Euro pallets on an artic trailer
Look up UN 1203 and check if 200 litres qualifies for ADR exemption
What's the chargeable weight for 2 boxes 120×80×100cm at 500kg?
Find the airline with AWB prefix 176
How many boxes 40×30×25cm fit on a Euro pallet?

Get Started in 2 Minutes

// FreightUtils API — JavaScript example
// Calculate loading metres for 10 Euro pallets

const response = await fetch(
  'https://www.freightutils.com/api/ldm?pallet=euro&qty=10'
);
const data = await response.json();

console.log(`LDM: ${data.ldm}`);
console.log(`Utilisation: ${data.utilisation_percent}%`);
console.log(`Fits: ${data.fits}`);

All endpoints work the same way. No auth, no signup. Full reference below ↓

POST/api/shipment/summaryShipment Summary (Composite)Try it →
Flagship Endpoint

Composite endpoint that chains CBM, chargeable weight, LDM, ADR compliance, and UK duty/VAT estimation into a single call. Accepts a unified Shipment object and returns comprehensive results based on transport mode.

Mode Parameter

ModeCalculations Included
roadCBM, LDM, pallet spaces, trailer utilisation, road chargeable weight (1 LDM = 1,750 kg), vehicle suggestion
airCBM, volumetric weight (1 CBM = 167 kg), air chargeable weight
seaCBM, revenue tonnes (W/M at 1 CBM = 1,000 kg), container suggestion
multimodalAll of the above — road, air, and sea calculations combined

Example Request

Mixed 3-item road shipment with a DG item and HS code:

curl -X POST "https://www.freightutils.com/api/shipment/summary" \ -H "Content-Type: application/json" \ -d '{ "mode": "road", "origin": { "country": "DE", "locode": "DEHAM" }, "destination": { "country": "GB", "locode": "GBFXT" }, "incoterm": "CIF", "freight_cost": 850, "insurance_cost": 120, "items": [ { "description": "Machine parts on Euro pallets", "length": 120, "width": 80, "height": 110, "weight": 480, "quantity": 6, "stackable": false, "pallet_type": "euro", "hsCode": "847989", "customs_value": 12000 }, { "description": "Cleaning solvent (DG)", "length": 60, "width": 40, "height": 50, "weight": 25, "quantity": 4, "unNumber": "1993" }, { "description": "Spare filters", "length": 40, "width": 30, "height": 20, "weight": 8, "quantity": 10, "stackable": true } ] }'

Example Response

{ "mode": "road", "item_count": 3, "totals": { "pieces": 20, "grossWeight": 3060, "volumeCBM": 7.28, "chargeableWeight": 3060, "billing_basis": "weight" }, "modeSpecific": { "loadingMetres": 4.0, "pallet_spaces": 10, "trailerUtilisation": 29.41, "suggested_vehicle": "13.6m Artic Trailer", "chargeable_weight_road": 7000 }, "compliance": { "hasDangerousGoods": true, "adrFlags": { "unNumbers": ["1993"], "totalPoints": null, "exemptionApplicable": null, "pointsBasis": "not_calculated", "pointsNote": "ADR 1.1.3.6 points are not calculated from a shipment summary: the only quantity here is gross package mass. Quantity on the ADR 1.1.3.6.3 basis: net kg for solids and liquefied, refrigerated or dissolved gases; litres for liquids; receptacle water capacity in litres for compressed or adsorbed gases and chemicals under pressure; articles as the mass of the articles without their packagings, in kg. Call adr_exemption_calculator with the quantity on that basis, and with the packing group or variant index for a multi-variant UN. Or send adr_quantity + adr_quantity_unit on every dangerous-goods line and this endpoint calculates the points itself.", "variantAmbiguous": ["1993"], "outOfScope": [] } }, "customs": { "hsCodesPresent": true, "canEstimateUkDuty": true, "dutyEstimate": { "cif_value": 12970, "duty_rate": "1.7%", "duty_amount": 220.49, "vat_rate": "20%", "vat_amount": 2638.1, "totalTaxes": 2858.59 } }, "warnings": [], "disclaimer": "Estimates only — verify with carrier and customs broker", "dataVersion": { "adr": "UNECE ADR 2025", "hs": "WCO HS 2022", "duty": "GOV.UK Trade Tariff API" } }

Pro-tier endpoint. Free access: 25 requests/day anonymous, 100/day with a free API key. Subscribe for higher limits.

GET/api/ldmLoading Metres CalculatorTry it →

Calculate the loading metres (LDM) required for a consignment on a UK/EU road freight trailer.

Parameters

ParameterTypeRequiredDescriptionDefault
lengthnumberYes*Pallet length in millimetres
widthnumberYes*Pallet width in millimetres
palletstringNo*Preset: euro, uk, half, quarter. Replaces length/width.
qtyintegerNoNumber of pallets1
stackablebooleanNoWhether pallets can be stackedfalse
stack2 or 3NoMax stack height (used when stackable=true)2
weightnumberNoWeight per pallet in kgnull
vehiclestringNoartic, rigid10 (DEPRECATED — removal in 3.0.0; still answers, returns a warning; use custom or artic), rigid75, luton, us53 (53ft US/Canada), us48 (48ft US), customartic
vehicle_lengthnumberNoCustom vehicle length in metres (required when vehicle=custom)

* Either pallet (preset ID) or both length and width must be provided.

Example Requests

12 Euro pallets on an artic:

GET /api/ldm?pallet=euro&qty=12&vehicle=artic

Custom dimensions, stackable, with weight check:

GET /api/ldm?length=1200&width=1000&qty=5&stackable=true&stack=2&weight=300&vehicle=rigid75

cURL example:

curl "https://www.freightutils.com/api/ldm?pallet=euro&qty=12&vehicle=artic"

US 53ft trailer:

curl "https://www.freightutils.com/api/ldm?pallet=euro&qty=20&vehicle=us53"

Response

{ "ldm": 4.8, "vehicle": { "name": "13.6m Artic Trailer", "length_m": 13.6, "max_payload_kg": 24000 }, "utilisation_percent": 35.29, "pallet_spaces": { "used": 12, "available": 33 }, "total_weight_kg": null, "fits": true, "warnings": [], "meta": { "inputs": { "length_mm": 1200, "width_mm": 800, "qty": 12, "stackable": false, "stack_factor": 2, "weight_per_pallet_kg": null, "vehicle": "artic" } } }
GET/api/cbmCubic Metres CalculatorTry it →

Calculate the cubic metre (CBM) volume of a shipment. Returns total CBM plus equivalents in cubic feet, litres, and cubic inches.

Parameters

ParameterTypeRequiredDescriptionDefault
lnumberYesLength of one piece in centimetres
wnumberYesWidth of one piece in centimetres
hnumberYesHeight of one piece in centimetres
pcsintegerNoNumber of identical pieces1

Example Request

5 boxes, 120×80×100 cm each:

curl "https://www.freightutils.com/api/cbm?l=120&w=80&h=100&pcs=5"
{ "cbm_per_piece": 0.96, "total_cbm": 4.8, "total_volume_m3": 4.8, "cubic_feet": 169.5106, "litres": 4800, "cubic_inches": 292913.8, "pieces": 5, "meta": { "inputs": { "length_cm": 120, "width_cm": 80, "height_cm": 100, "pieces": 5 } } }
GET/api/emissionsFreight Emissions (ISO 14083 / GLEC)Try it →

Estimate freight transport CO2e using the ISO 14083 / GLEC distance-based method: emissions = mass × distance × an open emission-intensity factor (kgCO2e/tonne-km). Provide actual gross mass, not chargeable/volumetric weight (a common air-freight mistake — see mass_basis). Returns well-to-wheel and tank-to-wheel emissions, the exact factor used, and a _source citing both the method and the specific open factor (DEFRA / EPA / ADEME). Each result also carries empty_running (the fleet-average factor already includes average empty running — don't double-count an empty return), representativeness (sea/air = low, high real-world variance), a human-readable summary, and _source.factor.last_verified. You provide the distance — this endpoint does not route. Best-effort estimate, not a verified carbon report; an unknown mode/sub_mode/region returns available:false with the covered options.

Parameters

ParameterTypeRequiredDescriptionDefault
massnumberYesShipment mass (in mass_unit)
mass_unitkg | tonnesNoUnit for masskg
distance_kmnumberYesTransport distance in km (caller-provided; not routed)
modeenumYesroad | rail | sea | air | inland_waterway
sub_modestringNoVehicle class (e.g. articulated, container ship)representative
regionuk | us | frNoFactor source: uk=DEFRA, us=EPA, fr=ADEMEper-mode
basiswtw | ttwNoWell-to-wheel or tank-to-wheelwtw

Example Request

10 tonnes by road, 500 km, UK (DEFRA), well-to-wheel:

curl "https://www.freightutils.com/api/emissions?mass=10&mass_unit=tonnes&distance_km=500&mode=road&region=uk&basis=wtw"
{ "available": true, "tonne_km": 5000, "factor": { "authority": "UK DEFRA / DESNZ — Greenhouse gas reporting: conversion factors", "edition": "2026", "region": "uk", "unit": "kgCO2e/tonne-km", "wtw": 0.12715, "ttw": 0.10356 }, "emissions": { "wtw_kgco2e": 635.75, "ttw_kgco2e": 517.8, "primary_kgco2e": 635.75, "basis_used": "wtw" }, "mass_basis": "actual_gross_mass", "empty_running": "sector_average_included", "representativeness": "medium", "confidence": "medium — fleet-average factor representative for the mode", "summary": "635.75 kgCO2e WTW for 10 t × 500 km by road (UK DEFRA 2026 fleet-average factor). Uses ACTUAL GROSS MASS, not chargeable/volumetric weight. Fleet-average empty running is already included — do not add a separate empty-return leg.", "methodology": "ISO 14083: 2023 / GLEC Framework v3.2", "_source": { "type": "methodology", "standard": "ISO 14083: 2023 / GLEC Framework v3.2", "source_url": "https://www.freightutils.com/methodology", "computed_by": "freightutils.com", "factor": { "authority": "UK DEFRA / DESNZ — Greenhouse gas reporting: conversion factors", "edition": "2026", "value": 0.12715, "unit": "kgCO2e/tonne-km", "basis": "wtw", "last_verified": "2026-06-26", "licence": "Open Government Licence v3.0" } } }
GET/api/validateIdentifier Validator (ISO 6346 / AWB / IMO)Try it →

Parse an arbitrary string (e.g. a booking line or email) to find and validate every freight identifier in it — shipping container (ISO 6346), air waybill (IATA modulus-7) and IMO ship number — or validate a single identifier by type. Returns per identifier: type, normalised form, valid (pass/fail), expected vs actual check digit, details (container owner/category; the AWB airline, or airline_candidates when the prefix is held by more than one record; IMO number) and a _source. Structural only — a valid check digit means well-formed, not that the entity exists.

Parameters

ParameterTypeRequiredDescriptionDefault
textstringNo*Arbitrary string to scan for container / AWB / IMO identifiers (parse mode)
valuestringNo*A single identifier to validate (typed mode); requires type
typecontainer | awb | imoNo*Identifier type for value

*Provide text (parse mode) OR value+type (typed mode).

Example Request

Parse a mixed string:

curl "https://www.freightutils.com/api/validate?text=Box%20CSQU3054383%20on%20IMO9074729"
{ "found": [ { "type": "container", "raw": "CSQU3054383", "normalised": "CSQU3054383", "valid": true, "check_digit": { "expected": 3, "actual": 3 }, "details": { "owner_prefix": "CSQ", "equipment_category": "U", "equipment_category_label": "Freight container", "serial": "305438" }, "_source": { "type": "methodology", "standard": "ISO 6346", "authority": "ISO", "computed_by": "freightutils.com" } }, { "type": "imo", "raw": "IMO9074729", "normalised": "IMO9074729", "valid": true, "check_digit": { "expected": 9, "actual": 9 }, "details": { "number": "9074729" }, "_source": { "type": "methodology", "standard": "IMO Ship Identification Number Scheme", "authority": "IMO", "computed_by": "freightutils.com" } } ], "disclaimer": "Structural validation only — a valid check digit means the identifier is well-formed, not that the container/shipment/vessel exists or is active." }
GET/api/ics2-checkICS2 Stop-Words Checker (EU goods descriptions)Try it →

Check a goods description against the official EU ICS2 stop-words list (unacceptable/vague terms for entry summary declarations). Returns the flagged terms — each with a note on whether it is the standalone description (automatic ENS rejection) or embedded (make the description more specific) — a clean boolean, a caveat, and a _source citing the EU list + legal basis. Reference only: not an ENS filing, not a compliance determination; the list is non-exhaustive and clean does not guarantee acceptance. No accepted/rejected verdict. The submitted description is not persisted or logged (response is no-store).

Parameters

ParameterTypeRequiredDescriptionDefault
descriptionstringYesThe goods description to check against the EU ICS2 stop-words list

Example Request

A vague standalone description:

curl "https://www.freightutils.com/api/ics2-check?description=general%20cargo"
{ "description": "general cargo", "flagged": [ { "term": "General cargo", "note": "Used as the entire goods description — a listed stop-word on its own is an automatic ENS rejection. Replace it with a specific product description..." } ], "clean": false, "caveat": "The EU ICS2 stop-words list is non-exhaustive ... a \"clean\" result does NOT mean the declaration will be accepted ...", "_source": { "type": "reference", "authority": "European Commission DG TAXUD — EU ICS2 (Advance Cargo Information System)", "legal_basis": "Commission Delegated Regulation (EU) 2015/2446, Annex B, data element 18 05 000 000 (Description of goods)", "list_in_force": "2026-05-04", "non_exhaustive": true, "retrieved_via": "freightutils.com" }, "disclaimer": "Reference check only. ... not an ENS filing, not a customs-compliance determination, and not legal advice ..." }
GET/api/airportsAirport Code Lookup (IATA / ICAO / name)Try it →

Look up an airport by IATA code (3 letters), ICAO code (4 chars), or free-text name / city search. Provide one of iata, icao or q. Returns the full record (both codes, name, type, municipality, region, country, coordinates, elevation); ambiguous name searches return ranked candidates. Optional type filter. Data: OurAirports (public domain), cross-checked vs OpenFlights + Wikidata. Reference only — not for navigation; verify current codes with IATA/ICAO.

Parameters

ParameterTypeRequiredDescriptionDefault
iatastringOne ofExact 3-letter IATA code (e.g. LHR)
icaostringOne ofExact 4-character ICAO code (e.g. EGLL)
qstringOne ofName / city / municipality search (min 2 chars)
typestringNoFilter: large_airport, medium_airport, small_airport, heliport, closed, seaplane_base

Example Request

curl "https://www.freightutils.com/api/airports?q=heathrow"
{ "count": 1, "results": [ { "ident": "EGLL", "iata_code": "LHR", "name": "London Heathrow Airport", "type": "large_airport", "municipality": "London", "region": "England", "country": "GB", "country_name": "United Kingdom", "latitude": 51.470748, "longitude": -0.459909, "elevation_ft": 83 } ], "disclaimer": "Airport reference data from OurAirports (public domain) ... not for navigation; verify current IATA / ICAO codes with the airport authority.", "_source": { "type": "reference", "authority": "OurAirports (community-maintained open data)", "dataset": "OurAirports 2026-06-24", "source_url": "https://ourairports.com/data/", "licence": "Public Domain (OurAirports — dedicated to the public domain)", "retrieved_via": "freightutils.com" } }
GET/api/nearest-airportNearest airports to a coordinateTry it →

Find the airports nearest to a caller-provided latitude/longitude, sorted by great-circle (haversine) distance with distance_km on each result. Coordinates are input only — never stored or logged. Optional radius_km, max_results (1–50, default 10) and type filter. Does NOT geocode place names or compute routes — pass coordinates you already hold.

Parameters

ParameterTypeRequiredDescriptionDefault
latnumberYesLatitude in decimal degrees (-90 to 90)
lonnumberYesLongitude in decimal degrees (-180 to 180)
radius_kmnumberNoMaximum distance in kilometres
max_resultsintegerNoResults to return (1–50)10
typestringNoFilter by airport type (e.g. large_airport)

Example Request

curl "https://www.freightutils.com/api/nearest-airport?lat=51.47&lon=-0.46&max_results=3&type=large_airport"
{ "count": 3, "results": [ { "ident": "EGLL", "iata_code": "LHR", "name": "London Heathrow Airport", "type": "large_airport", "country": "GB", "latitude": 51.470748, "longitude": -0.459909, "distance_km": 0.1 }, { "ident": "EGKK", "iata_code": "LGW", "name": "London Gatwick Airport", "type": "large_airport", "country": "GB", "latitude": 51.148102, "longitude": -0.190278, "distance_km": 40.6 }, { "ident": "EGGW", "iata_code": "LTN", "name": "London Luton Airport", "type": "large_airport", "country": "GB", "latitude": 51.874699, "longitude": -0.368333, "distance_km": 45.4 } ], "disclaimer": "Airport reference data from OurAirports (public domain) ... coordinates are the airport reference point, not for navigation.", "_source": { "type": "reference", "authority": "OurAirports (community-maintained open data)", "dataset": "OurAirports 2026-06-24", "licence": "Public Domain (OurAirports — dedicated to the public domain)", "retrieved_via": "freightutils.com" } }
GET/api/resolveResolve any freight identifierTry it →

The agent front door: one identifier-ish string in, typed, ranked, cited candidates out — without needing to know which lookup endpoint fits. Thirteen grammars all run on every query: UN numbers, AWB prefixes, airline IATA/ICAO, airport IATA/ICAO, UN/LOCODE, ISO 6346 container numbers (check digit computed), HS codes, Incoterms, ADR tunnel codes, ULD serials and ISO container size/type codes. Ambiguity is the product: colliding grammars return multiple candidates with an inspectable rank_basis on each, never a silent guess. Zero candidates is a validcount: 0 result — never an error. v1 resolves a single token (max 32 chars); free-text extraction is out of scope. This endpoint is envelope v1.1 native — responses are always enveloped; pass ?envelope=1 for the byte-stable v1 envelope.

Parameters

ParameterTypeRequiredDescription
qstringYesOne freight identifier — e.g. 176, UN1845, NLRTM, FOB, 22G1, MSKU1100810, 090111, D/E

Example Request

curl "https://www.freightutils.com/api/resolve?q=LHR"
{ "ok": true, "envelope_version": "1.1", "result": { "query": "LHR", "normalized": "LHR", "count": 2, "candidates": [ { "entity_type": "airport", "identifier_type": "airport_iata", "value_normalized": "LHR", "summary": "London Heathrow Airport — London, United Kingdom", "record": { "ident": "EGLL", "iata_code": "LHR", "name": "London Heathrow Airport", "type": "large_airport" }, "verification_status": "verified", "verification_basis": "dataset", "rank_basis": "dataset record hit · verification verified (dataset-level) · static prior airports #6 of 10 · tiebreak airport_iata/LHR", "canonical_url": "/airports", "api_url": "/api/airports?iata=LHR", "citation": { "text": "London Heathrow Airport (LHR) — OurAirports open data ..." } }, { "entity_type": "airline", "identifier_type": "airline_icao", "value_normalized": "LHR", "summary": "ICAO LHR — Al Ahram Aviation — Egypt", "record": { "airline_name": "Al Ahram Aviation", "icao_code": "LHR" }, "verification_status": "unchecked", "verification_basis": "record", "rank_basis": "dataset record hit · verification unchecked (record-level) · static prior airlines #1 of 10 · tiebreak airline_icao/LHR", "canonical_url": "/airlines/al-ahram-aviation", "api_url": "/api/airlines?icao=LHR", "citation": { "text": "ICAO LHR — Al Ahram Aviation ..." } } ] }, "confidence": { "level": "high", "basis": "deterministic" }, "citation": { "text": "\"LHR\" resolved to 2 candidates — top: airport_iata LHR, London Heathrow Airport ..." } }

Response Fields (per candidate)

FieldTypeDescription
entity_typestringWhat the candidate IS — dangerous_good, airline, airport, location, hs_classification, incoterm, container_type, container_equipment, uld_type, tunnel_restriction
identifier_typestringWhich grammar matched — un_number, awb_prefix, airline_iata, airline_icao, airport_iata, airport_icao, unlocode, container_number, hs_code, incoterm, tunnel_code, uld_type, iso_container_type
recordobject | nullCore fields from the matching dataset; null for a pattern-only match (e.g. an ISO type code not in the served set)
verification_statusstringNormalized across datasets — active_verified, verified, computed, partial, pending_verification, unchecked, unverified, historical, pattern_only
verification_basisstringrecord = from this record’s own provenance; dataset = the dataset-level claim (no record-level signal exists)
rank_basisstringWhy the candidate sits where it does — record hit > verification status > static dataset prior > stable tiebreak
canonical_url / api_urlstring?Where to go deeper — the record’s page and the dataset’s REST query; omitted when none exists
citationobjectThe matching dataset’s own citation for this record — authority stays attached to the record it describes
GET/api/chargeable-weightAir Freight Chargeable WeightTry it →

Calculate air freight chargeable weight — whichever is higher between actual gross weight and volumetric (dimensional) weight. Supports custom volumetric factors for all carriers.

Parameters

ParameterTypeRequiredDescriptionDefault
lnumberYesLength of one piece in centimetres
wnumberYesWidth of one piece in centimetres
hnumberYesHeight of one piece in centimetres
gwnumberYesTotal gross weight of all pieces in kg
pcsintegerNoNumber of identical pieces1
factorintegerNoVolumetric divisor: 6000 (IATA standard), 5000 (express carriers)6000

Example Request

2 pieces, 120×80×100 cm, 500 kg total, IATA factor:

curl "https://www.freightutils.com/api/chargeable-weight?l=120&w=80&h=100&gw=500&pcs=2&factor=6000"
{ "chargeable_weight_kg": 500, "basis": "actual", "gross_weight_kg": 500, "volumetric_weight_kg": 320, "volumetric_weight_per_piece_kg": 160, "cbm": 1.92, "ratio": 3.84, "factor": 6000, "pieces": 2, "meta": { "inputs": { "length_cm": 120, "width_cm": 80, "height_cm": 100, "gross_weight_kg": 500, "pieces": 2, "factor": 6000 } } }
GET/api/palletPallet Box Fitting CalculatorTry it →

Calculate how many boxes fit on a pallet using a layer-based algorithm. Returns boxes per layer, number of layers, total boxes, orientation used, and volume/weight analysis. Optional weight constraint caps the result at the pallet's maximum payload.

Parameters

ParameterTypeRequiredDescriptionDefault
plnumberYesPallet length in centimetres
pwnumberYesPallet width in centimetres
pmhnumberYesMaximum total stack height in centimetres (floor to top of cargo)
blnumberYesBox length in centimetres
bwnumberYesBox width in centimetres
bhnumberYesBox height in centimetres
phnumberNoPallet board/deck height in cm — deducted from usable height15
bwtnumberNoWeight per box in kg — enables weight constraint calculation
mpwnumberNoMaximum pallet payload weight in kg — caps result if weight exceeded
rotatebooleanNoAllow 90° rotation of boxes for best fit. Pass false to disable.true

Example Request

curl "https://www.freightutils.com/api/pallet?pl=120&pw=80&pmh=220&bl=40&bw=30&bh=25&bwt=5&mpw=1500"
{ "boxes_per_layer": 8, "layers": 8, "total_boxes": 64, "orientation": "original", "boxes_per_row": 3, "boxes_per_col": 2, "usable_height_cm": 205, "utilisation_percent": 62.5, "total_box_volume_cbm": 0.192, "pallet_volume_cbm": 1.968, "wasted_space_cbm": 1.776, "weight_limited": false, "total_weight_kg": 320, "remaining_weight_capacity_kg": 1180, "meta": { "inputs": { "pallet_length_cm": 120, "pallet_width_cm": 80, "pallet_max_height_cm": 220, "pallet_height_cm": 15, "box_length_cm": 40, "box_width_cm": 30, "box_height_cm": 25, "box_weight_kg": 5, "max_payload_weight_kg": 1500, "allow_rotation": true } } }
GET/api/adrADR 2025 Dangerous Goods LookupTry it →

Look up ADR 2025 dangerous goods by UN number, search by substance name, or filter by hazard class. The dataset contains 2,939 entries from the ADR 2025 Dangerous Goods List (Table A). Responses are cached for 1 hour (s-maxage=3600).

Query Modes

ParameterTypeDescriptionMax results
unstringExact UN number lookup. Accepts 1203, UN1203, or 01203.1
searchstringCase-insensitive partial match on the proper shipping name. Min 2 characters. Also accepts q as an alias.50
classstringFilter by ADR hazard class (e.g. 3, 6.1, 1.1).100

Provide exactly one parameter per request. Omitting all parameters returns a 400 with usage hints.

Example Requests

Exact UN number lookup:

curl "https://www.freightutils.com/api/adr?un=1203"
{ "count": 1, "results": [ { "un_number": "1203", "proper_shipping_name": "MOTOR SPIRIT or GASOLINE or PETROL", "class": "3", "classification_code": "F1", "packing_group": "II", "labels": "3", "special_provisions": "243 534 664", "limited_quantity": "1 L", "excepted_quantity": "E2", "transport_category": "2", "tunnel_restriction_code": "(D/E)", "hazard_identification_number": "33", "variant_index": 0, "variant_count": 1, "verified": false, "verification": { "status": "unverified", "basis": "dataset", "reason": "No row-level audit. Transcribed from the UNECE ADR 2025 Table A and held by the dataset-level checks only (56 anchor rows carry a row-level audit)." } } ], "meta": { "source": "ADR 2025 (UNECE, ECE/TRANS/352); given legal effect by EU Directive 2008/68/EC (consolidated)", "edition": "ADR 2025", "entries": 2939 } }

Per-row verification: every row carries verified and a verification block, always. verified: true (status verified, basis row) only where the row itself was audited field by field against the purchased UNECE Table A — then audited_at, the cited source count and the row’s sources[] ride along (56 anchor rows today, e.g. UN 1845, UN 1789). Every other row says verified: false with the reason no row-level audit: it was transcribed from the same Table A and is held by the dataset-level checks only. The envelope’s _source.provenance_status is the dataset-level claim; the row field is the row’s own. Silence is never verification.

Scope-flagged rows: 28 Table A entries carry a scope remark instead of a packing group. Those return packing_group: null plus not_subject_to_adr: true(with conditions_ref, e.g. "5.5.3" for UN 1845 dry ice) orcarriage_prohibited: true, and table_a_remark preserves the verbatim Table A text.

Search by substance name:

curl "https://www.freightutils.com/api/adr?search=acetone"
{ "count": 3, "results": [ ... ] }

Filter by hazard class:

curl "https://www.freightutils.com/api/adr?class=3"
{ "count": 50, "results": [ ... ] }
GETPOST/api/adr-calculatorADR 1.1.3.6 Exemption CalculatorTry it →

Calculate whether the ADR 1.1.3.6 small load exemption applies to a dangerous goods consignment. Supports single-substance GET queries and multi-substance POST requests. Checks both total points threshold (1,000) and per-substance quantity limits per ADR 1.1.3.6.3.

GET — Single Substance

ParameterTypeRequiredDescription
unstringYesUN number (e.g. 1203)
qtynumberYesQuantity on the ADR 1.1.3.6.3 basis: net kg for solids and liquefied, refrigerated or dissolved gases; litres for liquids; receptacle water capacity in litres for compressed or adsorbed gases and chemicals under pressure; articles as the mass of the articles without their packagings, in kg.
unitstringNoL or kg. Omit it and qty is taken as already on the 1.1.3.6.3 basis (unchanged behaviour). Supply it and it is checked against the dimension 1.1.3.6.3 counts for that row; a mismatch returns points: null, total_points: null, exempt: null and items[].basis_mismatch: true with a warning naming the right dimension — never a total computed from the wrong quantity. A row whose Table A column (15) carries no transport category (, or See SP 671 on the kit entry UN 3316) returns the same null total with items[].category_unresolved: true and a warning naming the column value.
basisstringNonet or gross. 1.1.3.6.3 counts the goods, never the packaging, so gross withholds the verdict in any unit.

Example — 200 litres of petrol:

curl "https://www.freightutils.com/api/adr-calculator?un=1203&qty=200"
{ "items": [ { "un_number": "1203", "proper_shipping_name": "MOTOR SPIRIT or GASOLINE or PETROL", "class": "3", "transport_category": "2", "quantity": 200, "multiplier": 3, "points": 600 } ], "total_points": 600, "threshold": 1000, "exempt": true, "has_category_zero": false, "has_quantity_exceedance": false, "warnings": [], "message": "1.1.3.6 exemption applies" }

POST — Multi-Substance Load

Request body:

POST /api/adr-calculator Content-Type: application/json { "items": [ { "un_number": "1203", "quantity": 200 }, { "un_number": "1090", "quantity": 50 } ] }

Response structure is identical to the GET endpoint, with multiple items in the array.

Multi-variant UN numbers: some UN numbers have more than one ADR Table A row (different packing groups / concentration bands with different transport categories — e.g. UN 1789 PG II vs PG III). Pass an optional packing_group(I/II/III, GET query or per item) or variant_index(from /api/adr) to pin one row. Without one, the response is HTTP 200 with human_review_required: true and a candidates[] list (no verdict) rather than a silently-guessed row. A packing_group that still leaves several rows is enough when those rows agree on class, transport category, scope and the 1.1.3.6.3 counted dimension (UN 1790 PG I: two concentration bands, both category 1) — the verdict is returned with items[].equivalent_variants and avariant_note saying which rows it holds for; only when the rows disagree(UN 2215 PG III: MOLTEN is category 0, the solid category 3) does it still ask for variant_index, and then candidates[] lists only that packing group’s rows. In a mixed load, an unpinned multi-variant line is withheld on its own (items[].withheld: true, points null) while every other line keeps its points; total_points is null until it is pinned, because 1.1.3.6.4 sums every line, and exempt is null too unless the resolved lines alone already disqualify the load — a “CARRIAGE PROHIBITED” entry, a category 0 entry, a per-substance maximum exceeded, or a partial sum already over 1,000 — in which case exempt is false with the usual message (and carriage_prohibited: true where that is the reason), since no packing group can undo those. candidates[] carry expected_unit, the 1.1.3.6.3 counted dimension — for UN 3375 PG II (liquid in L, solid in kg) that is the only field on which the rows differ. Only a load in which every line is ambiguous returns the no-items candidates[] shape.

Response Fields

FieldTypeDescription
packing_groupstring | nullResolved ADR packing group for the row (echoed for traceability)
variant_indexnumber | nullResolved ADR Table A variant index for the row (null on a withheld line)
transport_categorystring | nullADR transport category (0–4); null on a withheld line
multipliernumber | nullPoints multiplier for the category (null for cat 0, out-of-scope rows and withheld lines)
pointsnumber | nullquantity × multiplier
items[].withheldboolean?true when this line’s UN resolves to more than one Table A row and nothing pinned one — its row fields are null (never the first row’s), its candidate rows sit under candidates[] tagged with its item_index
items[].equivalent_variantsobject[]?Present when the supplied packing_group left several rows that agree on every field the verdict uses: the rows (variant_index, proper_shipping_name) this verdict holds for; variant_note says so in words and which row’s fields are shown
total_pointsnumber | nullSum of all substance points; null while any line is withheld or declared on a basis 1.1.3.6.3 does not count
exemptboolean | nulltrue if total ≤ 1,000 AND no cat 0 AND no quantity exceedance; null when no load verdict was reached (never false as a stand-in)
human_review_requiredboolean?true when at least one line is withheld — candidates[] then lists the withheld lines’ rows, each tagged with item_index and un_number
has_category_zerobooleantrue if any substance is transport category 0
has_quantity_exceedancebooleantrue if any substance exceeds its per-category max quantity
warningsstring[]Human-readable warning messages for limit violations and out-of-scope exclusions
not_subject_to_adrboolean?true when EVERY item is listed “NOT SUBJECT TO ADR” in Table A — the 1.1.3.6 math is not applied (there is nothing to be exempt from)
conditions_refstring?ADR section whose carriage conditions still apply to a not-subject load (e.g. 5.5.3 for UN 1845 dry ice)
conditionsobject[]?Verbatim ADR 2025 conditions for conditions_ref ({ref, heading, requirement, note?})
carriage_prohibitedboolean?true when the load contains a “CARRIAGE PROHIBITED” Table A entry — exempt is false; the load may not be carried by road under ADR

Scope verdicts: 28 ADR 2025 Table A rows carry a scope remark instead of a packing group. A load of such entries never enters the 1.1.3.6 points math — e.g.?un=1845&qty=100 (dry ice) returns not_subject_to_adr: true withmessage “Not subject to ADR (road). Section 5.5.3 applies: …” and the verbatim 5.5.3 conditions, while a “CARRIAGE PROHIBITED” entry (e.g. UN 0020) returns exempt: false with carriage_prohibited: true.

POST/api/adr/lq-checkADR Limited & Excepted Quantity CheckerTry it →

Check whether dangerous goods qualify for ADR Limited Quantity (Chapter 3.4) or Excepted Quantity (Chapter 3.5) concessions. Accepts up to 20 items per request and returns per-item pass/fail status against ADR Table A limits.

Request Body

FieldTypeRequiredDescription
modestringNolq (default) or eq
itemsarrayYes1–20 items to check
items[].un_numberstringYesUN number (e.g. 1203)
items[].quantitynumberYesQuantity per inner packaging
items[].unitstringNoml, L (default), g, or kg
items[].inner_packaging_qtynumberNoNumber of inner packagings per outer (EQ mode only)
items[].packing_groupstringNoI/II/III — disambiguates a UN number with more than one ADR Table A row (e.g. UN 1789). Ignored for single-row UN numbers.
items[].variant_indexnumberNoADR Table A variant index (from /api/adr) — pins one row when variants share a packing group (concentration bands).

A UN number that resolves to more than one ADR Table A row without a packing_group/variant_index returns HTTP 200 with human_review_required: true and a candidates[] list (no verdict) rather than checking a silently-guessed packing group. A packing_group that still leaves several rows is enough when those rows agree on class, column (7a), column (7b) and scope (UN 1790 PG I: two concentration bands, both LQ 0 / E0) — the item is answered with equivalent_variants and a variant_note; only when the rows disagree does it still ask for variant_index, listing only that packing group’s rows. In a batch, an unpinned multi-variant line is withheld on its own(status: "withheld", row fields null) while the other lines are answered;overall_status can never read qualifies while a line is withheld, andcandidates[] carries that line’s rows tagged with its item_index. Only a batch in which every line is ambiguous returns the no-items shape.

Example — check LQ for 0.5 L of petrol:

curl -X POST https://www.freightutils.com/api/adr/lq-check \ -H "Content-Type: application/json" \ -d '{"mode":"lq","items":[{"un_number":"1203","quantity":0.5,"unit":"L"}]}'
{ "mode": "lq", "overall_status": "qualifies", "items": [ { "un_number": "1203", "substance": "MOTOR SPIRIT or GASOLINE or PETROL", "class": "3", "packing_group": "II", "lq_limit": "1 L", "lq_limit_value": 1, "lq_limit_unit": "L", "eq_code": "E2", "quantity_entered": 0.5, "unit_entered": "L", "status": "within_limit", "reason": "0.5 L is within the LQ limit of 1 L per inner packaging" } ], "summary": { "total_items": 1, "qualifying": 1, "exceeding": 0, "not_permitted": 0 }, "references": { "adr_chapter": "3.4", "table": "3.2 Column 7a" } }

Response Fields

FieldTypeDescription
modestringlq or eq
overall_statusstringqualifies, does_not_qualify, partial, not_applicable (every item is listed “NOT SUBJECT TO ADR” in Table A — LQ/EQ provisions do not apply), or inconclusive (nothing in the batch could be compared). A batch holding any inconclusive or withheld item never reads qualifies.
items[]arrayPer-item results with substance info, limits, and pass/fail
items[].statusstringwithin_limit, exceeds_limit, not_permitted, not_subject (row is outside ADR scope — neither a pass nor a fail; scope rows also carry not_subject_to_adr / carriage_prohibited flags), inconclusive — Table A column (7a) states the limit in one dimension, and the quantity was sent in the other family (a mass against a volume limit, or the reverse). ADR states no density, so no verdict is given; re-send the quantity in the unit named by lq_limit_unit — or withheld: the line’s UN resolves to more than one Table A row and nothing pinned one. A withheld line carries withheld: true, an ambiguity_reason and null row fields (never the first row’s); its candidate rows are under candidates[] tagged with its item_index.
items[].equivalent_variantsobject[]?Present when the supplied packing_group left several rows that agree on class, column (7a), column (7b) and scope: the rows (variant_index, proper_shipping_name) this verdict holds for; variant_note says so in words and which row’s fields are shown
summaryobjectCounts of qualifying, exceeding, and not-permitted items, plus not_subject, inconclusive and withheld when non-zero
human_review_requiredboolean?true when at least one line is withheld — candidates[] then lists the withheld lines’ rows, each tagged with item_index and un_number
referencesobjectADR chapter and table references
GET/api/airlinesAirline Codes & AWB Prefix LookupTry it →

Search airlines by name, IATA code, ICAO code, AWB prefix, or country. The dataset contains6,357 airlines including 392 cargo airlines with AWB prefixes.

Query Modes

ParameterTypeDescriptionMatch
qstringGeneral search — matches name, codes, prefix, country. Smart: 2–3 digits match prefix only, 2–3 letters match IATA/ICAO only, 4+ chars search all fields.Smart
iatastringIATA 2-letter code (e.g. EK)Exact
icaostringICAO 3-letter code (e.g. UAE)Exact
prefixstringAWB 3-digit prefix (e.g. 176)Exact
countrystringCountry name (e.g. Germany)Partial

Example Requests

AWB prefix lookup:

curl "https://www.freightutils.com/api/airlines?prefix=176"
{ "count": 1, "results": [ { "slug": "emirates", "airline_name": "Emirates", "iata_code": "EK", "icao_code": "UAE", "awb_prefix": ["176"], "callsign": "EMIRATES", "country": "United Arab Emirates", "has_cargo": true, "aliases": ["Emirates SkyCargo"], "verified": false, "status": "unverified", "status_checked_at": "2026-08-11", "audited_at": "2026-05-13", "field_provenance": { "iata_code": [ { "url": "https://www.iata.org/en/publications/directories/code-search/?airline.search=EK", "publisher": "IATA (free code search, tie-break spot check)", "accessed_at": "2026-08-11" } ], "icao_code": [ { "url": "https://www.faa.gov/air_traffic/publications/atpubs/cnt_html/chap3_section_2.html", "publisher": "FAA Order JO 7340.2P Chg 3 (eff 2026-07-09), Chapter 3", "accessed_at": "2026-08-11" } ], "callsign": [ { "url": "https://www.faa.gov/air_traffic/publications/atpubs/cnt_html/chap3_section_2.html", "publisher": "FAA Order JO 7340.2P Chg 3 (eff 2026-07-09), Chapter 3", "accessed_at": "2026-08-11" } ], "operating_status": [ { "url": "https://www.skycargo.com/", "publisher": "Emirates SkyCargo", "accessed_at": "2026-08-11" } ] }, "sources": [ { "url": "https://www.faa.gov/air_traffic/publications/atpubs/cnt_html/chap3_section_2.html", "accessed_at": "2026-08-11" }, { "url": "https://www.iata.org/en/publications/directories/code-search/?airline.search=EK", "accessed_at": "2026-08-11" }, { "url": "https://www.skycargo.com/", "accessed_at": "2026-08-11" } ], "data_note": "Audited in the 2026-05-13 anchor pass, but awb_prefix carries no per-field carrier-published citation and the anchor sources include aggregators (which score zero). Held at unverified until a later wave re-sources it." } ], "meta": { "source": "IATA / ICAO airline code registries", "airlines": 6357, "last_verified": "2026-05-13" }, "_source": { "type": "reference", "authority": "IATA / ICAO airline code registries", "provenance_status": "pending-verification" } }

IATA code lookup:

curl "https://www.freightutils.com/api/airlines?iata=EK"

Name search:

curl "https://www.freightutils.com/api/airlines?q=emirates"

Response Fields

FieldTypeDescription
slugstringURL-friendly identifier
airline_namestringOfficial airline name
iata_codestring | null2-character IATA designator
icao_codestring | null3-character ICAO designator
awb_prefixstring[] | null3-digit AWB prefix(es) — array, some airlines have multiple
callsignstring | nullRadio callsign for ATC communication
countrystring | nullCountry of registration
has_cargobooleantrue if airline has AWB prefix(es)
aliasesstring[] | nullAlternative names (e.g. cargo division name)
statusstringactive_verified · historical · unverified — see the note below. Absent means never audited; absence is a claim of its own and is never coerced to a value
status_checked_atstringDate the status was last established
data_notestringPlain-language caveat about this record's evidence, where one applies
field_provenanceobjectPer-FIELD source citations — which page confirms which field. Aggregators score zero and never appear
sourcesobject[]Record-level union of the per-field citations (with access dates)
audited_atstringAudited records only — date of the audit
decision_rationalestringAudited records only — why the published values were accepted
verifiedbooleanDeprecated — computed mirror of status === 'active_verified', kept for one release. Absent (not false) on never-audited records. Read status

Verification status, and what it changes in the envelope

The airline dataset is still pending-verification, but individual records now carry their own evidence. active_verified means every identifier this API serves for that record — IATA code, ICAO code, AWB prefix — was confirmed against a page the carrier itself publishes (ICAO designator and callsign against FAA Order JO 7340.2), with the exact URL and access date in field_provenance. Aggregator and directory sites are scored at zero and never appear as evidence.historical means the carrier demonstrably ceased operating; the codes are kept as historical fact and may since have been reassigned. unverified means we looked and the evidence did not clear that bar — the honest default, not a slur on the record.

This is load-bearing in the envelope: an answer whose records are all active_verified is emitted at confidence.level: "high" with no advisory. Any other mix — one unverified record, one never-audited record, or an empty result — falls back to medium plus the PROVENANCE_PENDING warning. The lift is earned per answer and derived from the records in it, so it can never claim more than the evidence behind it.

Responses also carry a top-level _source citability envelope and a meta block whose airlines count is derived from the dataset at build time — never hand-typed.

GET/api/incotermsINCOTERMS 2020 LookupTry it →

Look up INCOTERMS 2020 trade terms. Returns all 11 terms by default, or filter by code or transport category. Each term includes seller/buyer responsibilities, risk and cost transfer points, insurance obligations, and practical guidance.

Parameters

ParameterTypeDescription
codestringINCOTERM code (e.g. FOB, CIF, DDP)
categorystringFilter by transport mode: any_mode or sea_only

Omit all parameters to return all 11 INCOTERMS 2020 terms.

Example Requests

Single term lookup:

curl "https://www.freightutils.com/api/incoterms?code=FOB"
{ "code": "FOB", "name": "Free on Board", "slug": "fob-free-on-board", "category": "sea_only", "summary": "Seller delivers goods on board the vessel at port of shipment. One of the most commonly used terms.", "seller_responsibility": "Deliver goods on board the vessel at named port. Export clearance. Loading costs.", "buyer_responsibility": "Main sea freight, insurance, import clearance, duties.", "risk_transfer": "When goods are on board the vessel at port of shipment.", "cost_transfer": "At port of shipment, once loaded on board.", "insurance": "No obligation on either party.", "export_clearance": "Seller.", "import_clearance": "Buyer.", "best_for": "Sea freight where buyer wants to arrange their own shipping and insurance. Very commonly used in international trade.", "watch_out": "Sea and inland waterway ONLY. Despite being widely used, FOB is technically incorrect for containerised cargo..." }

Filter by transport category:

curl "https://www.freightutils.com/api/incoterms?category=sea_only"

All terms:

curl "https://www.freightutils.com/api/incoterms"
GET/api/containersContainer Capacity ReferenceTry it →

Shipping container specifications — internal/external dimensions, weights, door openings, and pallet capacity for all 10 standard ISO container types. Optionally calculate how many items fit in a specific container.

Query Modes

ParameterTypeRequiredDescription
typestringNoContainer slug (e.g. 20ft-standard, 40ft-high-cube). Omit to list all.
lnumberNoItem length in cm (requires type + w + h)
wnumberNoItem width in cm
hnumberNoItem height in cm
wtnumberNoItem weight in kg
qtyintegerNoNumber of items

Example Requests

List all containers:

curl "https://www.freightutils.com/api/containers"

Single container specs:

curl "https://www.freightutils.com/api/containers?type=40ft-high-cube"

Loading calculation — how many 60×40×40cm boxes fit in a 40ft HC:

curl "https://www.freightutils.com/api/containers?type=40ft-high-cube&l=60&w=40&h=40&wt=15&qty=500"
{ "container": { "name": "40ft High Cube", "slug": "40ft-high-cube", ... }, "loading": { "fits_lengthwise": 20, "fits_widthwise": 5, "fits_height": 6, "max_items": 600, "requested_qty": 500, "fits": true, "total_weight_kg": 7500, "within_payload": true, "utilisation_percent": 63.2 } }
GET/api/convertUnit ConverterTry it →

Convert between freight-relevant units — weights, volumes, lengths, and freight-specific conversions (CBM to chargeable weight, CBM to freight tonnes).

Parameters

ParameterTypeRequiredDescription
valuenumberYesThe number to convert
fromstringYesSource unit code
tostringYesTarget unit code

Supported Unit Codes

GroupCodes
Weightkg, lbs, oz, tonnes, short_tons, long_tons
Volumecbm, cuft, cuin, litres, gal_us, gal_uk
Lengthcm, inches, m, feet, mm
Freightchargeable_kg (target only, from=cbm), freight_tonnes (target only, from=cbm)

Example Requests

Standard conversion:

curl "https://www.freightutils.com/api/convert?value=100&from=kg&to=lbs"
{ "input": { "value": 100, "unit": "kg", "name": "Kilograms" }, "result": { "value": 220.462442, "unit": "lbs", "name": "Pounds" }, "formula": "Kilograms × 2.204624 = Pounds" }

CBM to chargeable weight (IATA 6000 divisor):

curl "https://www.freightutils.com/api/convert?value=10&from=cbm&to=chargeable_kg"
{ "input": { "value": 10, "unit": "cbm", "name": "Cubic Metres" }, "result": { "value": 1666.7, "unit": "chargeable_kg", "name": "Chargeable Weight (kg)" }, "formula": "Cubic Metres × 166.67 = Chargeable Weight (kg)", "note": "IATA volumetric weight: 1 CBM = 166.67 kg (divisor 6000)..." }

CBM to freight tonnes (W/M rule):

curl "https://www.freightutils.com/api/convert?value=5&from=cbm&to=freight_tonnes"
GET/api/hsHS Code LookupTry it →

Search and browse Harmonized System (HS 2022) commodity codes. Supports text search by product description, exact code lookup with ancestor chain, and section browsing. Covers all 6,937 codes across 21 sections and 97 chapters.

Query Modes

ParameterTypeDescriptionMax results
qstringCase-insensitive search on descriptions and codes. Min 2 characters.50
codestringExact HS code lookup (2, 4, or 6 digit). Returns full details with ancestor chain and children.1
sectionstringBrowse by section (Roman numeral, e.g. II). Returns all chapters in that section.All

Provide exactly one parameter per request. Omitting all parameters returns a 400 with usage hints.

Example Requests

Search by description:

curl "https://www.freightutils.com/api/hs?q=coffee"
{ "query": "coffee", "results": [ { "hscode": "0901", "description": "Coffee, whether or not roasted...", "level": 4, "section": "II", "parent": "09" } ], "count": 12 }

Code lookup with ancestors:

curl "https://www.freightutils.com/api/hs?code=090111"
{ "hscode": "090111", "description": "Coffee; not roasted, not decaffeinated", "level": 6, "section": "II", "parent": "0901", "ancestors": [ { "hscode": "09", "description": "Coffee, tea, mate and spices", "level": 2 }, { "hscode": "0901", "description": "Coffee, whether or not roasted...", "level": 4 } ], "children": [], "sectionName": "Vegetable products" }

Browse section:

curl "https://www.freightutils.com/api/hs?section=II"
POST/api/consignmentMulti-Item Consignment CalculatorTry it →

Calculate total CBM, loading metres (LDM), volumetric and mode-specific chargeable weight across a multi-item mixed consignment — per-line and grand totals, plus objective advisory flags (implausible density, mode/option mismatch, dangerous-goods presence by UN number against the ADR 2025 reference, and ISO 6346 container / IATA AWB check-digit validity). Supports sea, air, and road modes. Canonical request/response schema: consignment.v1.json.

Best-effort deterministic calculation and reference data only. Verify all inputs. Not regulatory, customs, or dangerous-goods compliance advice — you remain responsible for classification, documentation and carrier acceptance.

Request Body (JSON)

FieldTypeRequiredDescription
modestringNoroad (default), air, or sea
linesarrayYes*1–50 canonical line objects (see below). Preferred.
itemsarrayYes*Legacy flat alias (dimensions in cm, weight in kg). *Provide lines or items.
optionsobjectNoair_volumetric_divisor (default 6000), container_number, awb_number

Canonical Line Object (lines[])

FieldTypeRequiredDescription
quantityintegerYesNumber of identical pieces
dimsobjectYes{ l, w, h, unit } — unit one of mm, cm, m, in
weightobjectYes{ value, unit } — unit one of kg, g, t, lb
stackablebooleanNoStack two-high (halves loading-metre footprint)
hs_codestringNoHS commodity code (6–10 digits)
un_numberstringNoUN number — triggers the dangerous-goods reference flag
descriptionstringNoItem label

Example Request

curl -X POST "https://www.freightutils.com/api/consignment" \ -H "Content-Type: application/json" \ -d '{ "mode": "air", "lines": [ { "quantity": 4, "dims": { "l": 120, "w": 80, "h": 100, "unit": "cm" }, "weight": { "value": 300, "unit": "kg" } }, { "quantity": 10, "dims": { "l": 60, "w": 40, "h": 50, "unit": "cm" }, "weight": { "value": 15, "unit": "kg" } } ]}'
{ "schema_version": "consignment.v1", "mode": "air", "air_volumetric_divisor": 6000, "per_line": [ /* cbm, gross_weight_kg, volumetric_weight_kg, ldm, chargeable_weight_kg, ... per line */ ], "totals": { "cbm": 5.04, "gross_weight_kg": 1350, "volumetric_weight_kg": 840, "chargeable_weight_kg": 1350, "billing_basis": "weight", "line_count": 2, "piece_count": 14 }, "flags": [], "disclaimer": "Best-effort deterministic calculation and reference data only. ..." }
POST/api/dutyUK Import Duty & VAT EstimatorTry it →

Estimate UK import duty and VAT for a commodity code using live GOV.UK Trade Tariff data. Accepts customs value, origin country, freight/insurance costs, and INCOTERM for CIF adjustment.

Request Body (JSON)

FieldTypeRequiredDescription
commodity_codestringYesHS/tariff code (min 6 digits)
origin_countrystringYesISO 2-letter country code (e.g. CN, DE)
customs_valuenumberYesGoods value in GBP
freight_costnumberNoFreight cost in GBP (added to CIF value)
insurance_costnumberNoInsurance cost in GBP (added to CIF value)
incotermstringNoINCOTERM (e.g. FOB, CIF, EXW)

Example Request

curl -X POST "https://www.freightutils.com/api/duty" \ -H "Content-Type: application/json" \ -d '{ "commodity_code": "847989", "origin_country": "CN", "customs_value": 10000, "freight_cost": 500, "insurance_cost": 50, "incoterm": "FOB" }'
{ "commodity_code": "847989", "origin_country": "CN", "cif_value": 10550, "duty_rate": "1.7%", "duty_amount": 179.35, "vat_rate": "20%", "vat_amount": 2145.87, "totalTaxes": 2325.22, "preferentialRate": false, "meta": { "source": "GOV.UK Trade Tariff API", "licence": "Open Government Licence v3" } }
GET/api/unlocodeUN/LOCODE LookupTry it →

Search and look up UN/LOCODE transport locations — 116,232+ seaports, airports, rail terminals, inland depots, and border crossings worldwide. Responses are cached for 24 hours.

Query Modes

ParameterTypeDescription
codestringExact UN/LOCODE lookup (e.g. GBLHR, NLRTM)
qstringSearch by name (e.g. rotterdam, heathrow)
countrystringFilter by country code (e.g. GB, NL)
functionstringFilter by function: port, airport, rail, road, icd, border
limitintegerMax results (1–100, default: 20)

Example Requests

Search by name:

curl "https://www.freightutils.com/api/unlocode?q=rotterdam"
{ "query": "rotterdam", "count": 3, "results": [ { "locode": "NLRTM", "name": "Rotterdam", "country": "NL", "subdivision": "ZH", "functions": ["port", "rail", "road"], "coordinates": { "lat": 51.92, "lon": 4.48 } } ], "meta": { "source": "UNECE UN/LOCODE 2025-1 (PDDL)", "total_entries": 116232 } }

Exact code lookup:

curl "https://www.freightutils.com/api/unlocode?code=GBLHR"

Filter by country and function:

curl "https://www.freightutils.com/api/unlocode?country=GB&function=port&limit=10"
GET/api/uldAir Freight ULD TypesTry it →

Look up air freight Unit Load Device (ULD) specifications. 16 types including LD3 (AKE), PMC main deck pallet, temperature-controlled containers, and more. Returns dimensions, weights, volume, and aircraft compatibility.

Parameters

ParameterTypeDescription
typestringULD code or slug (e.g. AKE, PMC). Omit to list all. Alias: code.
codestringAlias for type — accepted because the response field is named code
categorystringFilter: container, pallet, or special
deckstringFilter by deck: lower or main

An unrecognised query parameter returns 400 naming it, rather than being ignored — a filter this endpoint does not understand must never come back looking like a successful unfiltered query.

Example Requests

Single ULD lookup:

curl "https://www.freightutils.com/api/uld?type=AKE"

Filter by category:

curl "https://www.freightutils.com/api/uld?category=pallet"

All ULD types:

curl "https://www.freightutils.com/api/uld"
GET/api/vehiclesVehicle & Trailer TypesTry it →

Look up road freight vehicle and trailer specifications. 17 types covering articulated trailers, rigid trucks, and vans. Returns internal dimensions, payload limits, pallet capacity, and features.

Values carry their own semantics rather than leaving you to infer them. internal_dimensions.height is null on open-deck vehicles (flatbed, low-loader) — never 0, which would make a length × width × height calculation return zero volume. height_basis says what a null means (unbounded-open-top — no roof, so no constraint exists; varies-by-configuration; or unverified — a constraint exists but no trustworthy figure is held), and open_top states the physical fact separately. pallet_capacity_basis says whether a pallet count is floor geometry (floor-positions), capped by payload (weight-limited), an industry-convention figure, or unverified — which matters, because a floor-derived count and a payload-capped count are very different answers. max_pallet_height_mm gives the per-deck clearance on multi-deck vehicles and is null elsewhere, where the internal height is the usable clearance.

Parameters

ParameterTypeDescription
slugstringVehicle slug (e.g. standard-curtainsider). Omit to list all.
categorystringFilter: articulated, rigid, or van
regionstringFilter: EU or US

Example Requests

Single vehicle lookup:

curl "https://www.freightutils.com/api/vehicles?slug=standard-curtainsider"

Filter by category:

curl "https://www.freightutils.com/api/vehicles?category=articulated"

Filter by region:

curl "https://www.freightutils.com/api/vehicles?region=EU"

HTTP Status Codes

CodeMeaning
200Success — calculation result returned as JSON
400Bad Request — missing or invalid parameters. Check the error message in the response.
404Not Found — no results for the given query (airlines and ADR endpoints)
405Method Not Allowed — only GET (or POST for /api/adr-calculator, /api/adr/lq-check, /api/shipment/summary) is supported
500Internal Server Error — unexpected error, please report via GitHub

Field Naming

All endpoints use snake_case field names in responses (e.g. internal_length_cm, max_gross_kg). POST request bodies on /api/duty and /api/consignment accept either casing for backwards compatibility — snake_case is the documented form.

Rate Limiting

The API is free to use. Anonymous rate limit: 25 requests per day per IP. Free API key: 100 requests per day. Pro: 50,000 requests per month.

Error Responses

All endpoints return standard HTTP error codes with a descriptive JSON error message:

400Bad RequestMissing or invalid parameters
{"error": "Missing required parameter: l (length in cm)"}
404Not FoundResource does not exist
{"error": "No ADR entry found for UN number 9999"}
500Server ErrorUnexpected error
{"error": "Internal server error"}

Response Envelope (agent-facing, opt-in)

Every tool can also return a v1 response envelope — a thin, agent-facing wrapper that keeps the answer under result and adds the four things an autonomous agent needs to use and cite it safely: how much to trust it (confidence), where it came from(_source), a ready-to-quote line (citation), and — when relevant —what was corrected (normalized_input), advisories (warnings),blocking errors with a recovery hint (blocking_errors), and time-boxed validity(validity). It is fully described by the JSON Schema at /schemas/response-envelope.v1.json.

The envelope is opt-in. The flat legacy body (the answer at the top level) stays the default and is byte-unchanged, so existing REST consumers and the freightutils-mcp npm package are unaffected. Request the envelope explicitly with either ?envelope=1 or an Accept header:

curl "https://www.freightutils.com/api/airports?q=heathrow&envelope=1"
curl -H "Accept: application/vnd.freightutils.v1+json" "https://www.freightutils.com/api/airports?q=heathrow"

Envelope v1.1 — the freshness revision (additive, negotiated). Request it with ?envelope=1.1 (query parameter only). On editioned reference datasets the _source block gains dataset_edition (what this response is served from), authority_current_edition (the authority’s current in-force edition at last check) and checked_at (when the two were last compared) — and whenever the served edition trails the authority’s, the response carries an EDITION_LAG warning, advisory and never blocking. Continuous datasets (live UK duty, airport snapshots) legitimately omit the three fields. ?envelope=1 remains byte-stable v1. Schema: /schemas/response-envelope.v1.1.json.

curl "https://www.freightutils.com/api/unlocode?code=GBLHR&envelope=1.1"

On the hosted MCP server (/api/mcp) the envelope is returned as structuredContent (validating against each tool’s output schema) and serves v1.1 unconditionally — its declared schema updates with each deploy — while content[0].text keeps the flat legacy JSON so text-parsing clients are unaffected. envelope_version ("1" or "1.1") lets any consumer detect the shape.

Confidence model

confidence.level is high / medium / low, and confidence.basissays why. A numeric score (0–1) is present only when the basis ismatch_quality.

basisMeaningTypical level
deterministicPure computation (CBM, chargeable weight, LDM, conversions).high
provenanceReference-data lookup with an audit status.high (verified) / medium (provenance pending)
match_qualityFuzzy / ranked search — carries a score.from the score
freshnessComputed over live, time-sensitive data (e.g. UK duty rates).high

Warnings & blocking errors

warnings are non-blocking advisories; blocking_errors mean no answer was produced (ok: false) and each carries a recovery hint (an action, and often atool + params to retry with). Both use a stable UPPER_SNAKE code taxonomy; the keys are omitted entirely when empty (never []).

codeMeaning
RATE_LIMITEDQuota exceeded (maps to the 429 + Retry-After).
MISSING_INPUTA required parameter was absent.
INVALID_INPUTA parameter was present but malformed.
NOT_FOUNDAn exact lookup (code / id) matched nothing.
NO_MATCHA search / ranked query returned nothing.
FUZZY_BEST_MATCHAdvisory: the top result is a best-effort fuzzy match, not an exact hit.
PROVENANCE_PENDINGAdvisory: this dataset’s provenance is pending independent verification.
METHOD_NOT_ALLOWEDWrong HTTP method for the endpoint (e.g. GET on a POST-only tool).

Provenance, citation & validity

_source is the canonical provenance (name, checked date orrequest-time, and provenance_status: verified / pending-verification /computed / live). citation.text is a ready-to-quote line, with an optionalqualifier hedge for fuzzy or not-legal-advice answers. validity appears only on time-boxed tools (regulatory editions, live rates) and carries effective_from / effective_to /as_of.

Fuzzy best-match (GET /api/airports?q=heathrow&envelope=1):

{ "envelope_version": "1", "ok": true, "result": { "count": 2, "results": [ { "name": "Heathrow Airport", "iata_code": "LHR" } ] }, "confidence": { "level": "medium", "basis": "match_quality", "score": 0.66 }, "normalized_input": { "query": "heathrow", "normalized_query": "heathrow", "matched_field": "name" }, "warnings": [ { "code": "FUZZY_BEST_MATCH", "message": "Best of 2 ranked match(es) — matched on name." } ], "_source": { "name": "OurAirports (public domain)", "checked": "2026-06-24", "provenance_status": "verified", "source_url": "https://ourairports.com/data/" }, "citation": { "text": "Heathrow Airport (LHR) — OurAirports.", "qualifier": "Fuzzy best match for \"heathrow\"; verify the code." } }

Blocking error with a recovery hint (GET /api/adr?un=9999&envelope=1 → 404):

{ "envelope_version": "1", "ok": false, "result": {}, "confidence": { "level": "high", "basis": "provenance" }, "blocking_errors": [ { "code": "NOT_FOUND", "message": "UN number 9999 not found in the ADR 2025 dataset.", "recovery": { "action": "Check the UN number and retry, or search by name.", "tool": "adr_lookup", "params": { "un": "1203" } } } ], "_source": { "name": "UNECE (ADR 2025)", "checked": "2026-05-13", "provenance_status": "verified" }, "citation": { "text": "UN 9999 is not listed in UNECE ADR 2025." } }

Platform Commitments

Five pages that spell out what you can rely on:

Changelog
Every release, with an RSS feed.
Status
Current health plus 24h and 7d uptime.
Roadmap
Shipped, in progress, and what’s next.
Versioning Policy
Breaking-change contract and semver.
Deprecation Policy
3-month minimum notice with migration guides.

Source Code & Issue Reporting

The FreightUtils MCP server is open source. Report bugs, request features, or contribute on GitHub: github.com/SoapyRED/freightutils-mcp. For data corrections or API support, email contact@freightutils.com.