Trace
API reference

Trace docs

One API key. One endpoint. Clean, normalized tracking for any package.

Quickstart

Step 1

Create an account

Sign up and open the dashboard.

Step 2

Generate a key

Copy your trc_live key from the API keys page.

Step 3

Track a package

Send one POST request to /v1/track — that's the whole API.

First request
curl https://api.traceapi.dev/v1/track \
  -H "Authorization: Bearer trc_live_xxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"tracking_number":"YT2412345678"}'

Authentication

Every request goes to the same base URL. Send your API key as a Bearer token and keep live keys on your server, never in public frontend code.

Base URL
https://api.traceapi.dev
Header
Authorization: Bearer trc_live_xxxxxxxxxxxxx
POST

/v1/track

Track one shipment. Trace detects the likely carrier, fetches current tracking data, normalizes status, and returns carrier events when available.

Request body
{ "tracking_number": "YT2412345678" }
Response
{
  "tracking_number": "YT2412345678",
  "carrier": {
    "code": "yunexpress",
    "name": "YunExpress",
    "type": "dropshipping"
  },
  "status": "in_transit",
  "estimated_delivery": null,
  "events": [
    {
      "status": "in_transit",
      "description": "Departed origin facility",
      "location": "Shenzhen, China",
      "timestamp": "2026-07-03T09:30:00.000Z"
    }
  ],
  "cached": false,
  "request_id": "req_abc123",
  "last_checked_at": "2026-07-03T10:00:00.000Z"
}

Response fields

Every tracking response uses the same shape, whether the lookup is fresh, served from cache, or the carrier has no new scans yet.

FieldTypeDescription
tracking_numberstringThe tracking number exactly as submitted (trimmed).
carrier.codestringStable carrier identifier, e.g. yunexpress or ups. See carrier codes below; unknown when no pattern matches.
carrier.namestringHuman-readable carrier name.
carrier.typestringpremium, standard, domestic, regional, dropshipping, freight, or unknown.
statusstringNormalized shipment status. One of the statuses listed below.
estimated_deliverystring | nullISO 8601 date when the carrier provides one, otherwise null.
eventsarrayNormalized tracking history. Empty when the carrier has not published scans.
events[].statusstringPer-event status using the same enum as status.
events[].descriptionstringCustomer-safe, human-readable event copy.
events[].locationstring | nullCity / country when available, otherwise null.
events[].timestampstringISO 8601 scan time.
cachedbooleantrue when the response was served from the 30-minute cache.
request_idstringUnique id for this request; batch calls share one id across results.
last_checked_atstringISO 8601 time the upstream lookup last ran.
Statuses
pending           Label created; the carrier has not received the package yet.
in_transit        Package is moving through the carrier network.
out_for_delivery  On the vehicle for final delivery.
customs           Held in customs or clearance.
delivered         Delivered.
exception         Delivery problem: failed attempt, held, returned, or customs delay.
POST

/v1/track/batch

Track up to 20 shipments in one request. Batch calls count each tracking number toward usage.

Request body
{
  "tracking_numbers": [
    "YT2412345678",
    "1Z999AA10123456784"
  ]
}

Carrier codes

The carrier.code values returned by the API, with the tracking number prefixes used for auto-detection. Unrecognized numbers return unknown and Trace still attempts a lookup.

Carriers shown as Numeric use purely numeric tracking numbers with no alphabetic prefix, so they are matched by length and digit patterns rather than a prefix string.

CodeCarrierTypePrefixesRegions
yunexpressYunExpressdropshippingYTCN to US, CN to EU, CN to AU
epacketePacket (China Post)dropshippingLX, LY, LZCN to Global
4px4PXdropshipping4PX, FPXECN to US, CN to EU
cainiaoCainiaodropshippingCAIN, LPCN to Global
cjpacketCJ PacketdropshippingCJPAKCN to US, CN to EU
yanwenYanwendropshippingYWCN to Global
sfexpressSF ExpressdropshippingSFCN to Global
jtexpressJ&T ExpressdropshippingJTSEA to Global
suntekSunTek ExpressdropshippingSTKCN to US
dhlDHL ExpresspremiumJDGlobal
dhlecommerceDHL eCommercestandardGMGlobal
tntTNTpremiumGEGlobal
aramexAramexstandardARXMiddle East to Global
uspsUSPSdomestic9, LZ, ECUS
fedexFedExpremium7US, Global
upsUPSpremium1ZUS, Global
royalmailRoyal MaildomesticAA, ZZUK
dpdDPDdomesticNumericEU
glsGLSdomesticNumericEU
hermesEvri (Hermes)domesticNumericUK
postnlPostNLdomestic3SNL, EU
deutschepostDeutsche PostdomesticRRDE, EU
laposteLa PostedomesticCBFR, EU
correosCorreos (Spain)domesticPQES, EU
canadapostCanada PostdomesticNumericCA
australiapostAustralia Postdomestic33, 99AU
japanpostJapan PostdomesticEJ, RJJP
indiapostIndia PostdomesticEE, RRIN
ninjavanNinja VanregionalNVSEA
correiosbrCorreios (Brazil)domesticSS, OBBR
ontracOnTracregionalCUS
lasershipLaserShipregional1LSUS
chronopostChronopostpremiumXFR, EU
colissimoColissimodomestic6A, 6C, 8NFR, EU
mondialrelayMondial RelayregionalNumericFR, BE, ES, EU
bpostbpostdomesticCD, CEBE, EU
postnordPostNorddomesticNumericDK, SE, NO, FI
bringBringdomesticNumericNO, Nordics
swisspostSwiss Postdomestic99CH, EU
austrianpostAustrian PostdomesticNumericAT, EU
posteitalianePoste ItalianedomesticITIT, EU
inpostInPostregionalNumericPL, UK, EU
packetaPacketaregionalZCZ, SK, EU
yodelYodeldomesticJDUK
parcelforceParcelforcepremiumPB, PNUK, Global
anpostAn PostdomesticNumericIE
seurSEURregionalNumericES, PT
mrwMRWregionalNumericES, PT
cttCTT Portugal PostdomesticDA, LXPT
pttPTT PostadomesticRR, CPTR
israelpostIsrael PostdomesticNumericIL
saudipostSaudi PostdomesticNumericSA
emiratespostEmirates PostdomesticNumericAE
qatarpostQatar PostdomesticNumericQA
singaporepostSingapore PostdomesticRC, RF, RRSG, Global
hongkongpostHongkong PostdomesticRB, RCHK, Global
thailandpostThailand PostdomesticEE, RRTH
vietnampostVietnam PostdomesticNumericVN
poslajuPos LajudomesticEL, ERMY
phlpostPHLPostdomesticNumericPH
koreapostKorea PostdomesticEG, RRKR, Global
taiwanpostTaiwan PostdomesticNumericTW
nzpostNew Zealand PostdomesticNZNZ
chinapostChina PoststandardRA, RB, RC, EA, EBCN, Global
emsEMSstandardEGlobal
speedafSpeedafregionalSPCN, Africa, Global
delhiveryDelhiverydomesticNumericIN
bluedartBlue DartpremiumNumericIN
ekartEkartdomesticFMPC, FMPPIN
dtdcDTDCdomesticNumericIN
xpressbeesXpressBeesdomesticNumericIN
aramexauAramex AustraliaregionalNumericAU
fastwayFastwayregionalNumericAU, IE, NZ
tollTollregionalNumericAU, APAC
startrackStarTrackpremiumNumericAU
purolatorPurolatorpremiumNumericCA
canparCanparregionalNumericCA
tforceTForce FreightregionalNumericUS, CA
estesEstesfreightNumericUS
xpoXPOfreightNumericUS, EU
yrcYRC FreightfreightNumericUS
olddominionOld Dominion Freight LinefreightNumericUS
rlcarriersR+L CarriersfreightNumericUS
saiaSaiafreightNumericUS
estafetaEstafetadomesticNumericMX
redpackRedpackdomesticNumericMX
chilexpressChilexpressdomesticNumericCL
servientregaServientregaregionalNumericCO, EC
urbanoUrbanoregionalNumericAR, PE

Webhooks

Add an endpoint in the dashboard and Trace will POST signed tracking events to your app. You can test delivery from the Webhooks page before using it in production.

Events
tracking.updated
tracking.delivered
tracking.exception
Payload
{
  "event_id": "evt_abc123",
  "event_type": "tracking.updated",
  "created_at": "2026-07-03T10:00:00.000Z",
  "tracking": {
    "tracking_number": "YT2412345678",
    "status": "in_transit",
    "carrier": {
      "code": "yunexpress",
      "name": "YunExpress"
    },
    "events": []
  }
}
Signature headers
Trace-Event: tracking.updated
Trace-Timestamp: 1783072800
Trace-Signature: t=1783072800,v1=signature_hash
Verify (Node.js)
import crypto from "node:crypto";

const expected = crypto
  .createHmac("sha256", webhookSecret)
  .update(`${req.headers["trace-timestamp"]}.${rawBody}`)
  .digest("hex");

const trusted = req.headers["trace-signature"]?.includes(`v1=${expected}`);

Tracking playground

Test a live tracking request from the docs. Your key is sent only to the Trace API.

Client snippets

Copy-paste clients for your stack — no dependencies, same request shape the official Trace SDKs wrap.

Node.js
export async function trackPackage(apiKey, trackingNumber) {
  const response = await fetch("https://api.traceapi.dev/v1/track", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ tracking_number: trackingNumber })
  });

  if (!response.ok) throw new Error("Trace request failed");
  return response.json();
}
Python
import requests

def track_package(api_key, tracking_number):
    response = requests.post(
        "https://api.traceapi.dev/v1/track",
        headers={"Authorization": f"Bearer {api_key}"},
        json={"tracking_number": tracking_number},
        timeout=30,
    )
    response.raise_for_status()
    return response.json()

Limits and quotas

PlanRequests / minuteMonthly lookups
Free10500
Startup6010,000
Business12050,000
Enterprise300Unlimited

Errors

Every error returns the same shape: { "error": { "code", "message" } } with one of the codes below.

unauthorized401

Missing or invalid API key.

invalid_request400

Required request data is missing or malformed.

rate_limited429

Per-minute request limit exceeded.

monthly_quota_exceeded402

Monthly lookup quota exceeded for the current plan.

internal_error500

Trace could not complete the request.

Health check

GET /health
{ "ok": true, "service": "trace-api" }