Customer Data API

Delta polling & pagination

Syncing loads on a schedule — updatedSince, page math, and the one caveat that matters.

The sync recipe

Most integrations poll once or twice a day. The recommended loop:

  1. Call GET /api/v1/loads?updatedSince=<cursor>&page=1&pageSize=100.
  2. Follow hasMore through the pages, upserting each row into your system keyed by loadNumber.
  3. Track the maximum updatedAt you've seen across the sweep.
  4. Next run, set updatedSince to that maximum minus a small overlap (5 minutes is plenty). Duplicates are harmless because you're upserting.
curl -H "Authorization: Bearer $KEY" \
  "https://www.fr8factory.com/api/v1/loads?updatedSince=2026-07-09T18:55:00Z&pageSize=100"

How updatedSince behaves

  • Only loads with updatedAt >= updatedSince return.
  • updatedAt moves only on real value changes. Our internal sync machinery re-checks loads against our TMS every few minutes, but a re-check that changes nothing does not touch updatedAt — so your delta stays quiet when nothing happened.
  • Accepts an ISO 8601 date (2026-07-01) or datetime (2026-07-01T00:00:00Z). Unparseable values get a 400 with details.

What the feed covers

The endpoint serves loads scheduled to pick up within the last 12 months. Older loads age out of the feed — size your initial backfill accordingly (there's no way to reach further back through this API).

Filtering

All filters are optional and AND-composed with your customer scope, the 12-month window, and updatedSince:

ParameterEffect
statusRepeatable — ?status=InTransit&status=Covered matches any of the given statuses. Values must come from the closed status list.
pickupFrom / pickupToISO 8601; narrows pickDate to a span inside the 12-month window (a pickupFrom before the window floor doesn't reach further back).
deliveryFrom / deliveryToISO 8601; narrows on delDate.
poNumberExact match on your PO number.
billingRefNumberExact match on your billing reference.
curl -H "Authorization: Bearer $KEY" \
  "https://www.fr8factory.com/api/v1/loads?status=InTransit&status=Covered&pickupFrom=2026-06-01"

Pagination

  • page is 1-based; pageSize defaults to 100 and is capped at 100 (larger values clamp to 100).
  • The envelope carries page, pageSize, totalCount, and hasMore.
  • Rows are stably ordered by (updatedAt, id) ascending, so a page boundary always falls at the same place for a fixed dataset.

The page-skew caveat

Page-number pagination over live data has one inherent hazard: if a load is updated while you're mid-sweep, rows can shift between pages — its updatedAt changes, which moves it later in the sort order. In the worst case a row you haven't fetched yet moves behind your cursor and that sweep misses it. (Rows can also duplicate across pages, which your upsert already absorbs.)

You don't need to solve this in-sweep. The overlap in step 4 of the recipe is the fix: anything that moved mid-sweep has, by definition, a fresh updatedAt, so it's guaranteed to appear in your next poll's delta. With an overlapped updatedSince and loadNumber upserts, the loop is self-healing — a missed row stays missing for at most one poll interval.

If you're doing a one-time full backfill and want it airtight, run the sweep, then immediately re-query with updatedSince set to the time the sweep started. The second pass picks up anything that moved.

On this page