Access your Packy Tracking API dashboard.

Packy Tracking API (1.0.0)

Packy is a high-performance RESTful shipment tracking API and multicarrier tracking API designed to normalize delivery data across 1385+ carriers globally. Built for developers, e-commerce platforms, and logistics providers, it functions as a comprehensive package tracking API and parcel tracking API in a single integration.

Packy Tracking API offers a generous free tier (free package) for developers to start testing and tracking immediately with no credit card required.

Key features include carrier auto-detection, realtime updates via high-frequency webhooks, and unified delivery tracking API endpoints. Automate your post-purchase experience, reduce WISMO (Where Is My Order) tickets, and get normalized JSON status updates for any courier worldwide using our free plan or scalable commercial tiers.

How does your pricing work? What exactly is a "credit"?

1 credit = 1 unique tracking number.

Once you add a tracking number, that single credit covers the package's entire lifecycle. This includes everything from the moment it's created, through transit, final delivery, potential returns (if things don't go as planned), and even a little extra buffer time afterward—just to ensure you have all the data you need. No hidden fees, no per-request charges.

Parcel Statuses

Every parcel in the system is assigned a status that reflects the latest stage of its journey. As new tracking events arrive, the status is updated automatically. The possible statuses are:

Status Code Meaning
Awaiting pending Tracking is registered, but no carrier updates have been received yet.
Created create A shipping label/order has been created; the parcel is not yet moving.
Info Received info The carrier has received shipment information and is preparing to handle the parcel.
In Transit transit The parcel is on its way through the carrier's network.
Out for Delivery out-for-delivery The parcel is with a courier and scheduled for delivery today.
Delivered delivered The parcel has been successfully delivered to the recipient.
Failed Attempt fail A delivery attempt was made but was unsuccessful (e.g. recipient unavailable).
Exception exception A problem occurred during delivery that requires attention.
Cancelled cancel The shipment was cancelled.
Lost lost The parcel is reported as lost by the carrier.
Returning return The parcel is being returned to the sender.
Out for Delivery to Sender out-for-delivery-sender The returned parcel is out for delivery back to the original sender.
Delivered to Sender delivered-sender The returned parcel has been delivered back to the sender.
Tracking Ended end Tracking for this parcel has been closed; no further updates are expected.

Note: When a parcel has several events at once, the most significant status is shown (for example, Delivered takes precedence over In Transit).

Trackings

Create, list, retrieve, and delete shipment trackings.

List the account's trackings

Returns a paginated list of the account's trackings.

Authorizations:
BearerAuthApiKeyHeader
query Parameters
page
integer >= 1
Default: 1

Page number.

limit
integer >= 1
Default: 20

Page size.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "meta": {
    }
}

Create tracking(s)

Creates one or more trackings. The body is either a single object or an array of objects. Up to 40 trackings can be created per request.

Specifying the courier is optional: if courier_code is omitted, the courier is detected automatically from the tracking number.

This operation consumes account credits.

Behavior depends on ?async:

  • async=true (default) — trackings are created and refreshed in the background; the response contains the created trackings without events yet.
  • async=false — legacy sync mode: polls the carrier first, then subscribes. One credit is charged per item in the request (including when courier_code is unknown). Re-tracking an existing number is allowed. Unknown courier_code returns invalid_courier in errors. The response includes normalized events and crawler couriers (with icon URLs) per created item.

The response is a {data, errors} envelope:

  • 201 Created — all items were created;
  • 207 Multi-Status — partial success (both data and errors present);
  • 422 Unprocessable Entity — nothing was created, only errors.
Authorizations:
BearerAuthApiKeyHeader
query Parameters
async
boolean
Default: true

true (default) — return immediately with a short summary (TrackingSummary). false — wait for crawler poll and return full TrackingDetail (Crawler Trackcode + id + courier_code).

Request Body schema: application/json
required
One of
tracking_number
required
string
courier_code
string

Courier code; if omitted, it is detected automatically.

Responses

Request samples

Content type
application/json
Example
{
  • "tracking_number": "9400111899562537683144",
  • "courier_code": "usps"
}

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "errors": [
    ]
}

Get a tracking (full Trackcode)

Returns crawler Trackcode JSON with subscription overlay (id, courier_code). Same crawler blocks as webhook (events, source, couriers, info, …).

Authorizations:
BearerAuthApiKeyHeader
path Parameters
id
required
string

Tracking identifier.

Responses

Response samples

Content type
application/json
{
  • "id": "2f8b1c4d5e6f7a8b9c0d1e2f3a4b5c6d",
  • "courier_code": "usps",
  • "trackcode": "9400111899562537683144",
  • "status": "transit",
  • "updatedAt": "2026-05-26 14:30:00",
  • "origin_country": "string",
  • "destination_country": "string",
  • "info": {
    },
  • "events": [
    ],
  • "source": [],
  • "couriers": []
}

Delete a tracking

Deletes a tracking.

Authorizations:
BearerAuthApiKeyHeader
path Parameters
id
required
string

Tracking identifier.

Responses

Response samples

Content type
application/json
{
  • "code": "unauthorized",
  • "message": "Invalid or missing API key"
}

Couriers

List supported couriers and detect a courier by tracking number.

List supported couriers

Returns the list of supported couriers.

Authorizations:
BearerAuthApiKeyHeader

Responses

Response samples

Content type
application/json
[]

Detect courier by tracking number

Returns the list of possible couriers for the given tracking number.

Authorizations:
BearerAuthApiKeyHeader
Request Body schema: application/json
required
tracking_number
required
string

Responses

Request samples

Content type
application/json
{
  • "tracking_number": "MGRAE0003506539YQ"
}

Response samples

Content type
application/json
[]

Webhooks

Manage webhook subscriptions for delivery updates, and the contract for the deliveries we send to your endpoint.

Delivery contract

When a tracking you are subscribed to receives an update, we send an HTTP POST to every enabled subscription whose account owns that tracking. The request body is the tracking payload, forwarded verbatim — the same JSON, byte-for-byte, that GET /v1/trackings/{id} returns for the parcel. No wrapper object and no event field are added, so you must sign/verify against the raw request body (see below).

{
  "trackcode": "RA123456789CN",
  "status": "delivered",
  "origin_country": "CN",
  "destination_country": "UA",
  "events": [],
  "source": [
    {
      "code": "ukrposhta",
      "name": "Ukrposhta",
      "track": "RA123456789CN",
      "external_link": "https://track.ukrposhta.ua/tracking_RA123456789CN"
    }
  ]
}

Headers

Header Value
Content-Type application/json
X-Webhook-Id Delivery id. Stable across every retry of the same delivery — use it to deduplicate.
X-Webhook-Timestamp Unix time (seconds) of the delivery attempt.
X-Webhook-Signature sha256=<hex(HMAC-SHA256(secret, raw_body))>. Sent only when the subscription has a secret.

Signature verification

Compute the expected signature over the raw request body (before any deserialization, since the body is re-sent byte-for-byte) and compare in constant time:

expected = "sha256=" + hex(HMAC_SHA256(secret, raw_request_body))
compare(expected, X-Webhook-Signature)   // constant-time

Delivery, retries & idempotency

Any 2xx response is treated as a successful delivery. Any other status code, or a network/timeout error, triggers a retry. Because X-Webhook-Id is stable across retries, treat deliveries as at-least-once and deduplicate on that id.

List webhook subscriptions

Returns the account's webhook subscriptions.

Authorizations:
BearerAuthApiKeyHeader

Responses

Response samples

Content type
application/json
[
  • {}
]

Create a subscription

Creates a webhook subscription. url must be https. secret is never returned — its presence is surfaced via the has_secret field.

Authorizations:
BearerAuthApiKeyHeader
Request Body schema: application/json
required
url
required
string <uri>

Must use https.

secret
string

Secret used to sign deliveries (never returned in responses).

enabled
boolean
Default: true

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{}

Update a subscription

Partial update. Only the fields to be changed are sent. An empty secret clears the secret (has_secret=false).

Authorizations:
BearerAuthApiKeyHeader
path Parameters
id
required
integer <int64>

Subscription identifier.

Request Body schema: application/json
required
url
string <uri>
secret
string

An empty string clears the secret.

enabled
boolean

Responses

Request samples

Content type
application/json
{
  • "enabled": false
}

Response samples

Content type
application/json
{}

Delete a subscription

Authorizations:
BearerAuthApiKeyHeader
path Parameters
id
required
integer <int64>

Subscription identifier.

Responses

Response samples

Content type
application/json
{
  • "code": "unauthorized",
  • "message": "Invalid or missing API key"
}

Send a test delivery

Delivers a mock tracking payload to the subscription's endpoint and waits for the answer. Nothing is persisted and nothing is retried — use it to verify connectivity and signature checking after setting a subscription up.

The request looks like a real delivery (same headers, same Trackcode body, signed with the subscription's secret when it has one) with two markers so it can be told apart: the header X-Webhook-Test: true and an extra top-level "test": true field in the body. X-Webhook-Id is 0, since a test has no delivery row.

Disabled subscriptions can be tested too. A delivery that fails is still reported with HTTP 200 here — check ok and http_code in the body. Not billed.

Authorizations:
BearerAuthApiKeyHeader
path Parameters
id
required
integer <int64>

Subscription identifier.

Responses

Response samples

Content type
application/json
{
  • "ok": true,
  • "http_code": 200,
  • "duration_ms": 143,
  • "error": "http 500"
}