---
title: "Track parcels (billable)"
method: POST
path: "/v2/track"
tags: ["Tracking"]
---

# Track parcels (billable)

`POST /v2/track`

Track up to 5 parcels at once. Average response time 5 seconds per item. Supports 500+ carriers including USPS, UPS, FedEx, DHL, and more.

**Some carriers are limited to 1 item per request:**

The carriers listed below take substantially longer to look up, so a single synchronous request may contain **at most 1 item across all of them combined** — mixing two of them in one request is rejected as well. You can still batch them with other carriers: 1 item from this list + up to 4 items from any other carrier = 5 items total.

`us.ups`, `us.usps`, `us.fedex`, `us.dhl`, `us.dhl.express`, `us.dhl.ecommerce`, `intl.ups`, `intl.usps`, `intl.fedex`, `intl.dhl`, `gb.royalmail`, `gb.royalmail.special`, `gb.evri`, `gb.ups`, `ca.ups`, `ca.fedex`, `de.ups`, `es.ups`, `ie.ups`, `au.post`

This list may change. Exceeding the limit returns `INVALID_REQUEST`, and the error message names the exact carriers that triggered it. To track several items from these carriers, use `/v2/webhooks/register` (up to 100 items, asynchronous).

**Events without a time:** some carriers give only a date for certain events and no scan time (USPS does this for "In Transit to Next Facility" and "Pre-Shipment"). In that case `timestamp` is filled with noon on that date to stay valid ISO 8601, and the event carries `timeUnknown: true`. When the field is absent, the time came from the carrier and is real.

**When to use this endpoint:**
Use `/v2/track` for on-demand, one-off lookups — for example, when a customer checks their order status on your website.

**For continuous monitoring, use Webhooks instead.**
If you need to keep your database in sync with delivery status (e.g., updating order records, triggering notifications), register a webhook subscription via `/v2/webhooks/register` with `recurring: true`. The webhook approach is far more efficient — instead of polling repeatedly, you receive a push notification only when the status actually changes.

**Rate Limits:**
Each API key has per-minute and per-month request quotas based on your plan. When exceeded, the API returns HTTP 429. See response headers `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` for current usage.

| Plan | Requests/min | Requests/month |
|------|-------------|----------------|
| Starter | 30 | 10,000 |
| Pro | 60 | 30,000 |
| Business | 200 | 300,000 |

## Request body

- BulkTrackingRequest — Bulk tracking request
  - `trackingItems` TrackingRequest[], required
    - `carrier` string, required — Carrier code (e.g., 'kr.cj', 'us.fedex', 'de.gls')
    - `trackingNumber` string, required — Tracking number
    - `clientId` string — Client tracking ID (returned as-is in the response)
    - `postalCode` string — Postal code (required/optional for some carriers, e.g., GLS Germany)
    - `phoneNumber` string — Phone number (required/optional for some carriers)

## Response `200`

Successful tracking response

- BulkTrackingResponse — Bulk tracking response Responds with HTTP 200 OK, with success/failure status for each item within the results array.
  - `success` boolean, required
  - `results` TrackingResult[], required
    - union — Tracking result (Discriminated Union) Both single and bulk queries use this type for complete consistency.
      - TrackingSuccessResult — Tracking result (success) The type returned upon a successful Provider call.
        - `carrier` string, required — Carrier code
        - `trackingNumber` string, required — Tracking number
        - `clientId` string — Client tracking ID (the value provided at request time)
        - `data` object, required — Tracking result data
          - `deliveryStatus` 'pending' | 'in_transit' | 'out_for_delivery' | 'delivered' | 'failed' | 'returned' | 'cancelled' | 'unknown', required — Current delivery status (WhereParcel standardized) Represents the status of the most recent event. It is one of the 8 standard statuses of the TrackingStatus type. Note: It is named deliveryStatus to distinguish it from result.status. - result.status: API call result ("success" | "error") - deliveryStatus: Actual delivery status ("delivered" | "in_transit" | ...)
          - `estimatedDelivery` string — Estimated delivery date/time (optional, ISO 8601 format) The estimated delivery date/time provided by the carrier. When delivery is complete, it represents the actual delivery completion time. Note: The meaning of this field varies depending on status: - status is "delivered": Actual delivery completion time - status is otherwise: Estimated delivery date/time (subject to change) Examples: - "2026-01-30T18:00:00+09:00" (Korea timezone) - "2026-01-30T14:00:00-05:00" (US Eastern timezone) Note: Some carriers may not provide an estimated delivery date/time.
          - `events` object[], required — List of delivery events The delivery history sorted chronologically. The most recent event is located at the end of the array. Examples: - events[0]: First event (oldest record) - events[events.length - 1]: Last event (most recent record)
            - `timestamp` string, required — Event occurrence time (ISO 8601 format) The time standardized by WhereParcel. It converts the differing time formats of each carrier into ISO 8601 (e.g., "2026-01-30T14:30:00Z"). Examples: - Korea carrier: "2026-01-30 14:30" → "2026-01-30T14:30:00+09:00" (KST) - USPS: "January 30, 2026, 2:30 pm EST" → "2026-01-30T14:30:00-05:00" (EST) - DHL: "30.01.2026 14:30" → "2026-01-30T14:30:00Z"
            - `timeUnknown` boolean — Whether the time portion of `timestamp` is an estimate rather than a value the carrier provided (optional). Some carriers give only a **date** for certain events and no scan time — USPS does this for events like "In Transit to Next Facility" and "Pre-Shipment". In that case `timestamp` is filled with noon on that date to stay valid ISO 8601, and this field is `true`. Absent or `false` means the carrier supplied the time itself.
            - `status` 'pending' | 'in_transit' | 'out_for_delivery' | 'delivered' | 'failed' | 'returned' | 'cancelled' | 'unknown', required — Delivery status (WhereParcel standardized) The standardized value of the TrackingStatus type. It converts the various status codes of carriers into 8 standard statuses. Examples: - Logen "Picked up" → "in_transit" - FedEx "DEL" → "delivered" - USPS "Out for Delivery" → "out_for_delivery" - Japan carrier "配達完了" → "delivered"
            - `location` string — Location information (WhereParcel standardized, optional) The physical location where the event occurred. It standardizes the differing location notation methods of each carrier as much as possible. Examples: - South Korea: "Seoul, Gangnam-gu, Teheran-ro", "Busan, Buk-gu logistics center" - USPS: "NEW YORK NY DISTRIBUTION CENTER" - FedEx: "MEMPHIS, TN" - DHL: "Frankfurt Gateway" Note: Some carriers may not provide location information.
            - `description` string, required — Event description (WhereParcel standardized) A detailed description of the event. It is converted to English and provided whenever possible. Examples: - South Korea: "Arrived at Namgwanak" - FedEx: "Delivered. Signed by: JOHN DOE" (original: "Delivered. Signed by JOHN DOE") - USPS: "Out for delivery" (original: "Out for Delivery") - DHL: "Customs clearance completed" (original: "Customs clearance completed")
            - `rawStatus` string — Carrier original status code (optional) Preserves the status code/text actually displayed by the carrier exactly as-is. Provided so that customers can compare it with the carrier's official site. Examples: - Logen: "Picked up", "In transit", "Delivered" - CJ Logistics: "Received", "Out for delivery", "Delivered" - FedEx: "PU" (Picked up), "IT" (In transit), "DEL" (Delivered) - USPS: "Acceptance", "In Transit", "Delivered" - DHL: "Picked up", "Clearance event", "Delivered" Note: This field matches the "Status" column on the carrier's official site.
            - `rawDescription` string — Carrier original description (optional) Preserves the event description actually displayed by the carrier exactly as-is. Unlike description, it is provided in its original form without language conversion or standardization. Examples: - Logen: "Arrived at Namgwanak" - CJ Logistics: "Loaded onto line-haul" - FedEx: "Delivered. Left at front door. Signature Service not requested." - USPS: "Out for Delivery, Expected Delivery by 8:00pm" - DHL: "Clearance event - Import clearance completed" - China carrier: "【广州市】已到达 广州转运中心" (original Chinese preserved) Note: This field matches the "Details" column on the carrier's official site.
          - `from` object — Origin information (optional) Location information from which the parcel was shipped. For privacy protection, the name may be masked. Examples: - name: "Hong*dong" (individual: masked), "Coupang" (company: original) - address: "Seoul, Gangnam-gu", "Tokyo, Japan" Note: Some carriers may not provide origin information.
            - `name` string
            - `address` string
          - `to` object — Destination information (optional) Location information to which the parcel will be delivered. For privacy protection, the name and detailed address may be masked. Examples: - name: "Kim*su" (individual: masked), "ABC Company" (company: original) - address: "Seoul, Seocho-gu" (detailed address excluded) Note: Some carriers may not provide destination information.
            - `name` string
            - `address` string
          - `signedBy` string — Signer information (optional) The name of the person who signed upon delivery completion. Mainly provided by North American/European carriers (FedEx, UPS, DHL, etc.). Examples: - "JOHN DOE" - "Front Desk" - "Receptionist" Note: - Most Korean/Asian carriers do not provide this. - Unattended delivery (e.g., doorstep delivery) is null.
          - `weight` object — Weight information (optional) The weight of the parcel. Mainly provided for international shipments. Examples: - { value: 2.5, unit: "kg" } - { value: 5.5, unit: "lb" } Note: Most domestic parcels do not provide weight information.
            - `value` number, required
            - `unit` 'kg' | 'lb' | 'g', required
          - `deliveryInstructions` string — Delivery instructions (optional) Delivery-related instructions requested by the customer. Examples: - "Please leave it with the security office if I am away" - "Leave at front door" - "Call before delivery" - "配達前に電話してください" (Japanese) Note: Most carriers do not provide this.
          - `lastUpdated` string, required — Last update date/time (ISO 8601 format) The time when WhereParcel retrieved the data from the carrier. Used to determine the freshness of cached data. Examples: - "2026-01-30T15:30:00Z" Note: This value is not the carrier's last update time.
        - `billable` boolean, required — Whether a charge is applied (true on Provider call, false on cache hit)
        - `cached` boolean, required — Whether it was a cache hit
        - `duplicate` boolean — Whether it is a duplicate within the same request
        - `hints` object[] — Additional input field notice (when an optional field is not provided)
          - `type` string, required — Notice type (always 'additional_field_available')
          - `fieldName` string, required — Additional input field name (e.g., 'postalCode', 'phoneNumber')
          - `message` string, required — Notice message
        - `status` string, required
      - TrackingErrorResult — Tracking result (failure) The type returned upon a failed Provider call. (e.g., tracking number not found, carrier API error, etc.)
        - `carrier` string, required
        - `trackingNumber` string, required
        - `clientId` string
        - `status` string, required
        - `error` object, required — Error detail information included in the API error response
          - `code` 'UNAUTHORIZED' | 'AUTH_MISSING_API_KEY' | 'AUTH_INVALID_CREDENTIALS' | 'AUTH_INVALID_TOKEN' | 'TOKEN_EXPIRED' | 'TOKEN_REVOKED' | 'KEY_INACTIVE' | 'FORBIDDEN' | 'RATE_LIMIT_EXCEEDED' | 'MONTHLY_LIMIT_EXCEEDED' | 'INVALID_REQUEST' | 'REQUEST_INVALID_FORMAT' | 'REQUEST_INVALID_CARRIER' | 'REQUEST_INVALID_TRACKING_NUMBER' | 'REQUEST_INVALID_CLIENT_ID' | 'REQUEST_VALIDATION_FAILED' | 'TRACKING_NOT_FOUND' | 'INVALID_TRACKING_NUMBER' | 'NOT_FOUND' | 'DUPLICATE_URL' | 'ENDPOINT_NOT_FOUND' | 'ENDPOINT_INACTIVE' | 'EMAIL_ALREADY_REGISTERED' | 'CODE_EXPIRED' | 'MAX_ATTEMPTS_EXCEEDED' | 'INVALID_CODE' | 'INVALID_TOKEN' | 'EMAIL_MISMATCH' | 'API_KEY_LIMIT_EXCEEDED' | 'INVALID_WEBHOOK_URL' | 'INVALID_DATE' | 'INVALID_BODY' | 'POSTAL_CODE_REQUIRED' | 'POSTAL_CODE_INVALID_FORMAT' | 'PHONE_NUMBER_REQUIRED' | 'PHONE_NUMBER_INVALID_FORMAT' | 'INTERNAL_ERROR', required — Error code (for programmatic handling)
          - `message` string, required — Human-readable error message
          - `retryAfter` number — Wait time until retry when the rate limit is exceeded (seconds)
        - `billable` boolean, required
        - `cached` boolean, required
        - `duplicate` boolean
  - `summary` object, required
    - `total` number, required
    - `success` number, required
    - `failed` number, required
    - `usageIncremented` number, required
    - `duplicates` number
  - `requestLimit` RequestLimit — API rate limit information. The per-second request limit applied to all API endpoints (server protection)
    - `second` string, required
  - `trackingQuota` TrackingQuota — Parcel tracking usage information. Billing usage applied only to the /track endpoint (per minute/day/month)
    - `minute` string, required
    - `day` string, required
    - `month` string, required
  - `processingTime` number
  - `coldStart` boolean

## Other responses

- `401` — Authentication failed - API key missing or invalid
- `429` — Rate limit exceeded

---

[API](https://skmtc.dev/whereparcel/apis/whereparcel-api.md) · [All operations](https://skmtc.dev/whereparcel/apis/whereparcel-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/whereparcel/whereparcel-api/revisions/629880970b8b/schema)
