How to Track a Shipment

Follow every parcel from label to doorstep. Easyship turns each courier's events into one set of statuses, which you can pull through the API or receive by webhook.

What it does
Gives you status and checkpoint updates for parcels
Works with
Easyship labels and labels from other providers
Delivery
Webhooks (recommended) or polling
Scopes
track · track_3p (advanced)

Which tracking do I need?

EASYSHIP LABEL
I bought the label through Easyship

Nothing to set up. The shipment is tracked automatically.

Scope: public.track:read

Go to Easyship labels →

OTHER LABEL
My label comes from another provider

Register the tracking number once. Easyship then tracks it for you.

Scope: public.track_3p:read / :write (advanced; ask your account manager)

Go to other providers →

Base URL: https://public-api.easyship.com/2024-09

Track Easyship shipments

cURLGET /2024-09/shipments/trackings
curl "https://public-api.easyship.com/2024-09/shipments/trackings?easyship_shipment_id=ESUS100000001&include_checkpoints=true" \ -H "Authorization: Bearer $EASYSHIP_TOKEN"
Query parameterUse it to
easyship_shipment_idGet one shipment
platform_order_numberLook up by your order number
include_checkpoints=trueInclude the full event history

Track shipments from other providers

Step 1
Check the courier
GET /trackings/supported_couriers
→
Step 2
Register tracking
POST /trackings
→
Step 3
Get updates
webhook or GET /trackings/{id}
  1. Check that the courier is supported

    You can filter by umbrella_name (e.g. DHL) and origin_country_alpha2.

    cURLGET /2024-09/trackings/supported_couriers
    curl "https://public-api.easyship.com/2024-09/trackings/supported_couriers?umbrella_name=DHL&origin_country_alpha2=US" \ -H "Authorization: Bearer $EASYSHIP_TOKEN"
    Response200 OK
    { "courier_services": [ { "id": "137a79e8-ae1c-4369-9855-44cf8ff784c4", "name": "DHL Express", "umbrella_name": "DHL", "origin_country_alpha2": "US" } ] }

    Pass the id you get back as courier_service_id in the next step.

    Courier not in that list? If Easyship doesn't generate labels for the courier, send courier_service_name instead of courier_service_id. Get the exact name from GET /trackings/external_courier_services.
  2. Register the tracking number
    cURLPOST /2024-09/trackings
    curl -X POST https://public-api.easyship.com/2024-09/trackings \ -H "Authorization: Bearer $EASYSHIP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tracking_number": "0114827", "courier_service_id": "137a79e8-ae1c-4369-9855-44cf8ff784c4", "platform_order_number": "ORDER-1001", "origin_address_id": "01563646-58c1-4607-8fe0-cae3e33c0001", "destination_address": { "line_1": "123 Test Street", "city": "Singapore", "state": "Singapore", "postal_code": "247964", "country_alpha2": "SG", "contact_name": "Foo Bar", "contact_phone": "+65 6910 1185", "contact_email": "[email protected]" }, "items": [{ "description": "iPhone", "quantity": 1 }] }'
    FieldNotes
    tracking_numberRequired. The courier's tracking number
    courier_service_id or courier_service_nameOne is required: the ID from step 1, or the name from /trackings/external_courier_services
    platform_order_numberYour order number. It makes it easy to match webhooks to orders
    origin_address_id or origin_addressA saved address ID, or the full address
    destination_addressThe full delivery address
    itemsdescription and quantity
  3. Check status
    cURLGET /2024-09/trackings
    # One tracking curl https://public-api.easyship.com/2024-09/trackings/{tracking_id} \ -H "Authorization: Bearer $EASYSHIP_TOKEN" # Many, filtered (origin_country_alpha2, destination_country_alpha2, source=easyship|external, tracking_state) curl "https://public-api.easyship.com/2024-09/trackings?tracking_state=active" \ -H "Authorization: Bearer $EASYSHIP_TOKEN"

    Each tracking in the response has two status fields:

    • status: the latest courier status in plain words, e.g. In Transit or Delivered. Show this to your users.
    • tracking_state: the state of Easyship's tracking for this number. Use it to filter, e.g. ?tracking_state=active.

    Each tracking also includes eta_date and a tracking_page_url.

    tracking_stateMeaning
    activeEasyship is tracking the number and recording courier updates. Trackings created with POST /trackings start here.
    completedThe courier reported a final event (e.g. delivered). No more updates.
    pendingMulti-leg shipments only: a later leg that is waiting for the previous leg to complete. It becomes active automatically.
    createdRegistered but tracking hasn't started yet. Rarely seen through the API.
    overwritten_by_adminReplaced or switched off by Easyship, e.g. after a tracking number correction. No longer updated.

Get updates by webhook

Every poll counts against your API rate limits. In production, subscribe to these events with the Webhooks API instead:

EventFires when
shipment_tracking_status_changedThe overall status changes, e.g. in transit → delivered
shipment_tracking_checkpoints_createdThe courier reports a new event
Why webhooks? You hear about changes sooner, make fewer API calls, and stay well clear of rate limits.

FAQ and testing

Why hasn't status changed when my tracking is active?
Couriers can take a few hours to show the first event for a new tracking number. If nothing appears, check the tracking number and the courier you sent.
Why does POST /trackings return an error about the courier?
Send either a courier_service_id from step 1 or an exact courier_service_name from /trackings/external_courier_services.
How do I test status changes in sandbox?
Use Update a tracking (sandbox only) to simulate a status change. It isn't available in production.

Did this page help you?