List orders and follow their progress
There is no event push at v1, so following an order means reading it. This page
covers listing orders, reading progress.eta, and how often to come back.
List a page of orders
GET /orders returns your orders newest
first, 50 per page by default and at most 50 per page.
curl "https://api.spoke.com/connect/v1/orders?maxPageSize=50" \
-H "Authorization: Bearer <api-key>"
Each entry is a full Order. This one is abridged to the fields that matter for tracking:
{
"orders": [
{
"id": "orders/W3kJpzQm9q4LV2BdH7sNuT",
"createdAt": 1778508502,
"courier": "couriers/QyZ8mp34RFlIfwREp1js",
"submitted": true,
"submittedAt": 1778511103,
"readOnly": true,
"source": "retailer",
"progress": {
"trackingLink": "https://track.spoke.com/t/abc123",
"eta": {
"status": "available",
"value": {
"estimatedArrivalAt": 1779013800,
"estimatedEarliestArrivalAt": 1779012600,
"estimatedLatestArrivalAt": 1779015000
}
},
"attempted": false,
"attemptedAt": null,
"state": "unattempted",
"routeStartDate": { "year": 2026, "month": 5, "day": 16 },
"routeStarted": true,
"succeeded": false,
"photoUrls": [],
"signatureUrl": null,
"signeeName": null,
"driverProvidedRecipientNotes": null
}
}
],
"nextPageToken": "MTc3ODUwODUwMnxXM2tKcHpRbTlxNExWMkJkSDdzTnVU"
}
To walk past the first page, pass nextPageToken back as pageToken and keep
every other query parameter the same. Stop when nextPageToken comes back
null, and only then. A page can be short while more results exist, so a short
page is not the end. See
On List endpoints for the full
rules.
To find one order you already have your own reference for, filter on the external id you set at creation:
curl "https://api.spoke.com/connect/v1/orders?filter.externalId=PO-2026-04-21-1138" \
-H "Authorization: Bearer <api-key>"
External ids are not unique-constrained, so if you have reused one this returns every order carrying it.
Read the progress
progress is null until the courier puts the order on a route. Once it is
there, the whole delivery story is in that object.
progress.eta is a tagged union, so read status before value:
status | What it means |
|---|---|
available | value holds the arrival estimate and its earliest and latest bounds. |
pending | The order is on a route but the route is not optimized yet. Come back later. |
restricted | The courier's subscription does not include ETAs. It will never become available for this order. |
{ "status": "pending" }
{ "status": "restricted", "restrictionCode": "subscription_not_supported" }
Treat restricted as final. Retrying does not change it, and a client that
loops waiting for available will loop forever.
ETAs are recomputed on every read from the route's current state, so they move as the driver does. They are not stored on the order, which is why you get a fresh number each time rather than a stale one.
The rest of progress reads in this order:
routeStartedturnstruewhen the driver starts the route. Before that an ETA is a plan, not a live estimate.attemptedturnstrueonce the driver has been to the address, successful or not, withattemptedAtholding when.succeededsays whether the delivery worked, andstatesays what actually happened, for exampledelivered_to_recipientorfailed_not_home.photoUrls,signatureUrl, andsigneeNamecarry the proof the driver captured. A photo URL can answer not-found for a short while after the attempt, because the driver's app is still uploading it. Retry rather than treating it as missing.
trackingLink is the page the recipient can follow. It is safe to pass on, unlike
the shipping label link.
How often to poll
Reads are limited to 10 requests per second per IP (see On Rate-Limiting), which is generous enough that good sense, not the limit, is what should set your cadence. Every read recomputes ETAs from the live route, so a tight loop is expensive for both sides and buys you nothing: a driver's arrival estimate does not meaningfully change second to second.
What works:
- Poll the list endpoint, not each order. One request covers 50 orders. A
loop of
GET /orders/{id}spends 50 requests against your read budget for the same information. - Once a minute per page is plenty while you have orders on a started route, and it stays far under the read limit.
- Poll only the orders you are actually watching. An order with
progressstillnullhas nothing new to report until the courier routes it, and an order withattempted: truehas already told you how it ended. - Back off outside delivery hours. Nothing moves overnight.
Handle a 429 with backoff and jitter even though a once-a-minute poll stays
well under the limit, so an unexpected burst on your side does not lock you out.
See On Rate-Limiting.