Tutorial · Developer Tutorials

Estimate International Shipping Before You Ship: HIOBuy Shipping Quotes Demo

Open-source Next.js demo that lists shipping channels, inspects channel configuration, and estimates international shipping costs with the HIOBuy Fulfillment API — key stays server-side.

HHioBuy Developer Team8 min read

HIOBuy Shipping Quotes — channels, estimates, Fulfillment API

For teams that already have a website or app, and need international shipping estimates from a China warehouse on their frontend — not a white-label store, and not a C-end shipping calculator.

You already have a site, or you are about to build one. Catalog search works. The extra piece some teams want is a shipping estimate the shopper (or the operator) can see before anything leaves the warehouse: which channels are available, what a declared parcel would cost, how long it might take. On your domain. In your UI. With the API key never leaving your server.

That is a fulfillment problem, not a storefront-skin problem. HIOBuy powers the China-side warehouse and the shipping APIs behind it. Your backend lists channels, inspects a channel, and posts a quote. The open-source demo is github.com/hiobuy/shipping-quotes (MIT). Live demo: shipping-quotes.demo.hiobuy.com. The rest of this post is how to run it, what the three Fulfillment shipping calls actually do, and where the demo stops.

Welcome UI of the HIOBuy shipping quotes demo

Welcome UI at shipping-quotes.demo.hiobuy.com. Banner: “Demo data only.” Copy: “Get a shipping estimate before you ship.” Left: shipment details (weight in kilograms, dimensions in centimeters). Right: shipping options, empty until you submit.

The public page is sample data for demonstration — prices, transit times, channels, availability, services, and exchange rates on that host are not real quotes or carrier commitments. Do not point production traffic at it as a proxy.

Fulfillment shipping vs the catalog demos

Same developer portal. Different surface.

IntegrationBase pathWhat it is forThis demo
Product catalog REST/v1/products/*1688 / Taobao / Weidian listings on your siteNo — that is the starter
Agent tool calling/ai/v1/*Conversational searchNo — that is ai-product-finder
Fulfillment shipping/v1/fulfillment/shipping/*Warehouse channels and international quotesYes

HIOBuy is the China warehouse and fulfillment layer. You still own the frontend, the checkout later, and whatever you wrap around an estimate. This post does not pitch a consumer shipping widget. It is the B-end path: your app talks to Fulfillment shipping so a buyer on your site can see a budget before you create a shipment.

Canonical quotes reference: hiobuy.com/en/api-docs/fulfillment-freight-estimate. OpenAPI: api.hiobuy.com/openapi.json.

What the demo actually does

Three APIs. Mapped 1:1 to local Route Handlers.

PurposeHIOBuy APILocal demo endpoint
Channel catalogGET /v1/fulfillment/shipping/channelsGET /api/shipping/channels
Channel detailGET /v1/fulfillment/shipping/channels/{shipping_channel_code}GET /api/shipping/channels/{code}
Shipping estimatePOST /v1/fulfillment/shipping/quotesPOST /api/shipping/quotes

It does not create a shipment, take payment, or track a parcel. Those stay out of this repository on purpose.

Compatibility alias on the quotes call: POST /v1/fulfillment/shipments/freight/estimate (same behavior). Prefer /v1/fulfillment/shipping/quotes.

Quote results for South Korea in the shipping quotes demo

Declared parcel at shipping-quotes.demo.hiobuy.com: South Korea, postal code 10001, 2 kg, 30 × 20 × 10 cm, declared value 75 USD. The UI reports five options. Visible rows carry quote_status COMPLETE, estimated totals in the demo display currency, and a business-day transit range. Sample data only.

Hands-on: get a key, clone, run

1. Get an API key

Create a developer account at developers.hiobuy.com, create an App, and issue an API key. The README uses a sandbox or test key for local work.

An API key alone is not enough if the App is not authorized for warehouse fulfillment. Official errors on this surface include 403 WAREHOUSE_AUTH_INVALID (warehouse authorization missing, rejected, or expired) and 403 FULFILLMENT_MODE_NOT_SUPPORTED (App uses self fulfillment). Fix those in the developer portal — not in the demo UI.

2. Clone the demo

MIT licensed. Next.js. Node.js 20 or newer, plus pnpm.

git clone https://github.com/hiobuy/shipping-quotes.git
cd shipping-quotes

3. Keep the key on the server

cp .env.example .env.local

Then set:

HIOBUY_API_KEY=your_api_key_here

Optional:

HIOBUY_API_BASE_URL=https://api.hiobuy.com
HIOBUY_DEFAULT_LANGUAGE=en
SHIPPING_DISPLAY_CURRENCY=USD
SHIPPING_CNY_EXCHANGE_RATE=0.14

Never commit .env.local. Never put the key in frontend code, and never prefix it NEXT_PUBLIC_. The browser never calls HIOBuy directly.

4. Install and run

pnpm install
pnpm dev

Open http://localhost:3000. You should get the same calculator UI as the public demo, against your own App.

How the request actually travels

Browser
   │  GET  /api/shipping/channels
   │  GET  /api/shipping/channels/{code}
   │  POST /api/shipping/quotes
   ▼
Next.js Route Handlers  (app/api/shipping/*)
   │  Read server-only HIOBUY_API_KEY
   │  Authorization: Bearer <key>
   ▼
HIOBuy Fulfillment API
   GET  /v1/fulfillment/shipping/channels
   GET  /v1/fulfillment/shipping/channels/{shipping_channel_code}
   POST /v1/fulfillment/shipping/quotes
   ▼
JSON to the browser  (no API key)

lib/hiobuy.ts is the authenticated server client. Frontend code should call /api/shipping/*, not the HIOBuy host. Channel codes are URL-encoded before they go into the public channel-detail path.

If you already have a frontend in another stack, keep the same split: secret on the server, three thin proxies, your UI as the only thing users see.

The server also checks both HTTP status and the JSON body. Some warehouse business failures return HTTP 200 with "success": false. A missing or country-filtered channel is normalized locally as CHANNEL_NOT_FOUND. Transport errors use the standard API error object.

Channels and channel detail

Start with the catalog:

GET /v1/fulfillment/shipping/channels
Authorization: Bearer <server-only key>
Language: en

That is the list of shipping channels visible to the warehouse tied to the current key — not a global carrier directory.

Then inspect one channel:

GET /v1/fulfillment/shipping/channels/{shipping_channel_code}

Identify channels (and services) by stable code, not name. The demo requests expansions together so the detail dialog does not need a second click:

regions,services,rate_cards,rules

What you get back is configuration, not a price for this parcel:

  • warehouse identity
  • supported regions and countries
  • billing basis, billing model, quantity unit, range, minimum charges, rounding
  • multi-package scope and volumetric-weight rules
  • reference rate-card rows
  • available services
  • channel rules

Reference rate cards explain how a channel is priced. Do not replay them in the browser and call that a quote. Shipment-specific availability and estimated charges come from POST /v1/fulfillment/shipping/quotes.

Transit time

Prefer the structured fields:

reference_transit_time.min_business_days
reference_transit_time.max_business_days

A single value displays as 5 business days. A range displays as 5–8 business days. The compatibility text value is fallback when structured numbers are missing. Transit times are estimates, not delivery guarantees.

Optional services and rules

Channel services are MANDATORY or OPTIONAL. Percentage values are already percentage points: 10 means 10%, not 1000%. Pricing may sit on base freight, declared value, a fixed amount per shipment, a fixed amount per box, or chargeable-weight units.

The quote request always sends services.channel. When every optional service is removed, send an empty array — do not omit the field, or provider defaults can come back.

Rule aggregation:

ModeMeaning
SUM_ALLEvery matched, calculable charge is added
HIGHEST_ONLYOnly the highest applicable charge is used (equal highs are equivalent)

cap: null or cap.amount: 0 means no cap. Pending or inapplicable rules do not contribute to the quote total.

Quotes: two request formats, one money unit

POST /v1/fulfillment/shipping/quotes
Authorization: Bearer <server-only key>
Content-Type: application/json
Language: en

Language lives in the header only. Do not put language in the JSON body.

Quotes are budgets, not locked prices. Warehouse weighing, volume measurement, re-packing, and service review can change the final amount. A COMPLETE quote is still not a price lock.

Money and units

All money amount values are integer CNY fen. Top-level monetary_unit is always CNY_minor. Weight is KG. Dimensions are CM.

{
  "amount": 1250,
  "currency": "CNY"
}

That is ¥12.50, not ¥1,250.

Declared value uses the same shape. Omit declared_value when unknown. Do not send 0 to mean unknown.

Format A — single-package shorthand

Use this when you know destination and estimated total weight, but not how the warehouse will split boxes.

{
  "destination": {
    "country_code": "KR",
    "postal_code": "04524"
  },
  "weight_kg": 2.05,
  "length_cm": 30,
  "width_cm": 20,
  "height_cm": 10
}

length_cm, width_cm, and height_cm are all-or-nothing. Missing dimensions may produce PARTIAL or REVIEW_REQUIRED on volumetric channels.

Format B — known packages

Use packages[] when box count and per-box measurements are already known.

{
  "destination": {
    "country_code": "KR"
  },
  "packages": [
    {
      "reference": "box-1",
      "weight": {
        "value": 2.05,
        "unit": "KG"
      },
      "dimensions": {
        "length": 30,
        "width": 20,
        "height": 10,
        "unit": "CM"
      }
    }
  ]
}

Do not mix Format A shorthand fields (weight_kg, length_cm, width_cm, height_cm) with packages[]. A mixed request is 400 VALIDATION_ERROR.

The Gateway maps Format A to a single warehouse package. Format B is validated as declared. Final split and measured dimensions are still determined after warehouse processing.

Always read quote_status

Do not decide from available alone.

quote_statusavailableMeaning
COMPLETEtrueFull budget you may display. Still not locked.
PARTIALtrueMissing inputs. Read warnings; add dimensions, postal code, or declared value.
REVIEW_REQUIREDtrueWarehouse measurement or human review is required.
UNAVAILABLEfalseThis channel cannot quote. The HTTP request still succeeded.

UNAVAILABLE on one quote is not an HTTP error. success can be true while some quotes in quotes[] are unavailable. Keep those rows server-side so you can explain unavailable_reason.code. The demo hides unavailable quotes from the shopper-facing list; the contract is still intact on the server.

When total is null, do not treat known_total as a complete quote.

Destination discovery is coverage, not a guarantee

The demo destination selector is not a hard-coded world list. The server requests:

GET /v1/fulfillment/locations?status=ACTIVE

then collects, validates, normalizes, and deduplicates data[].supported_destinations[]. Values are ISO 3166-1 alpha-2.

That is destination coverage reported by the active fulfillment locations visible to this warehouse and key. It does not guarantee that a particular shipment can use a destination. Postal code, weight, dimensions, product characteristics, selected services, channel rules, and warehouse configuration can still block a quote.

POST /v1/fulfillment/shipping/quotes is authoritative for shipment-specific availability and pricing.

Display currency is demo-only FX

The API always returns CNY minor units. The demo converts for the UI with two env vars:

SHIPPING_DISPLAY_CURRENCY     default USD
SHIPPING_CNY_EXCHANGE_RATE    default 0.14

Interpretation: 1 CNY = 0.14 display-currency units. Conversion:

(CNY minor units ÷ 100) × exchange rate

Declared value goes the other way before the quote call:

entered display currency ÷ exchange rate = CNY

Example at 0.14: 14 USD → 100 CNY → 10000 fen.

This is a static demo rate, not a live foreign-exchange feed. It can go stale. If you need a real FX source or a markup, that lives in your application — the Fulfillment shipping API does not convert currency for you.

The live page prints the same caveat next to declared value: “Fixed demo rate: 1 CNY ≈ 0.14 USD.”

Where it stops

Read this before you treat the demo as a shipping product.

It is an integration example for Fulfillment shipping (/v1/fulfillment/shipping/*). It does not include:

  • Shipment creation
  • Payment
  • Tracking
  • A production rate limiter, auth wall, or abuse controls on the public proxy routes

Quotes are estimates based on declared data (estimate_type is DECLARED). Final charges may change after warehouse measurement and packing. Transit times are estimates. Destination lists are warehouse-scoped. Rate cards are not a substitute for POST .../quotes.

The public demo at shipping-quotes.demo.hiobuy.com is for evaluation, behind sample-data copy. Clone the repo and run it against your own key if you are wiring this into a real frontend.

HIOBuy Shipping Quotes Demo on GitHub

The MIT repo README at github.com/hiobuy/shipping-quotes: channel discovery, channel detail, and international shipping estimates, with the key kept server-side.

Where to go next

Clone it, put a key in .env.local, and hit localhost. If international shipping estimates on your own frontend — backed by a China warehouse — are the missing piece, that is the whole job this demo is for.


Next step

If you already understand the workflow, move to the API reference for exact request and response fields, or open the developer console to create your application.

Ready to build?

Open the API documentation or create your HioBuy developer application.

Related guides

Tutorial

How to Put Official Taobao and 1688 Product Data on Your Own Website

If you already have a site, the missing piece is live Taobao and 1688 catalog data on your own frontend — search, image search, paste a link, SKU detail — without scraping. Walk through HIOBuy’s open-source Next.js starter and the four Product API calls it actually makes.