> ## Documentation Index
> Fetch the complete documentation index at: https://docs.haulstow.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Poll delivery status

> Repeatedly check a delivery's status without wasting requests.

Polling means asking the API for the same delivery every few seconds to see whether its status changed. It is the simplest way to start, but [webhooks](/webhooks) are better for production because HaulStow sends an update only when something happens.

## One status check

```bash theme={null}
curl "$HAULSTOW_BASE_URL/deliveries/$HAULSTOW_DELIVERY_ID" \
  --header "Authorization: Bearer $HAULSTOW_API_KEY"
```

Read these fields from `data`:

* `status`: the current delivery state;
* `updated_at`: when the delivery last changed.

## A safe polling loop

If your system cannot receive webhooks yet, poll `GET /deliveries/{delivery_id}` every 10–15 seconds.

1. Save the last `status` and `updated_at` values.
2. Fetch the event timeline only when either value changes.
3. Stop polling at `delivered`, `failed`, or `cancelled`.
4. Add random jitter when polling many deliveries so requests do not synchronize.

Do not repeatedly list the full delivery collection to track one delivery.

```javascript Node.js theme={null}
const terminalStatuses = new Set(["delivered", "failed", "cancelled"]);

async function waitForDelivery(deliveryId) {
  while (true) {
    const response = await fetch(
      `https://sandbox.developer.haulstow.co/v1/deliveries/${deliveryId}`,
      { headers: { Authorization: `Bearer ${process.env.HAULSTOW_API_KEY}` } },
    );
    const body = await response.json();

    if (!response.ok) {
      throw new Error(`${body.error?.code}: ${body.message}`);
    }

    console.log(body.data.status);
    if (terminalStatuses.has(body.data.status)) return body.data;

    // Wait 10–15 seconds so many deliveries do not all poll together.
    const delayMs = 10_000 + Math.floor(Math.random() * 5_001);
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }
}
```

<Warning>
  Do not poll every second. It adds load, uses your rate-limit allowance, and rarely gives a better customer experience.
</Warning>

## Rate-limit handling

Every response includes:

* `RateLimit-Limit`: maximum requests in the current window
* `RateLimit-Remaining`: requests left in the current window
* `RateLimit-Reset`: Unix time in seconds when the window resets

On `429 RATE_LIMIT_EXCEEDED`, stop making requests for the number of seconds in `Retry-After`. If that header is absent, wait until the Unix timestamp in `RateLimit-Reset`. For a temporary `5xx` response, increase the delay before each retry and save `X-Request-ID` for diagnostics.

## Paginating a list

Listing endpoints return one page at a time. `meta` explains whether another page exists:

Cursor-based list endpoints return this metadata:

```json theme={null}
{
  "meta": {
    "limit": 25,
    "has_more": true,
    "next_cursor": "opaque-value"
  }
}
```

When `has_more` is `true`, pass `next_cursor` back unchanged:

```bash theme={null}
curl "$HAULSTOW_BASE_URL/deliveries?limit=25&cursor=opaque-value" \
  --header "Authorization: Bearer $HAULSTOW_API_KEY"
```

Do not decode or edit a cursor. When `has_more` is `false` or `next_cursor` is `null`, you reached the final page.
