Pagination and sync
The cursor, page limits and incremental synchronisation via updatedSince.
Lists (GET /proposals/search, GET /proposals/my) return the same wrapper:
{
"data": [ /* … listings … */ ],
"nextCursor": "eyJvIjoyMH0",
"total": 137
}
data— the page of results.nextCursor— an opaque string for the next page, ornullwhen there are no more.total— how many records match the filter in total.
Page size
The limit parameter defaults to 20 and maxes out at 100. A larger value is not an
error — it is silently clamped. limit=500 returns 100 rows, not a 400.
Walking pages
Pass the nextCursor you received as cursor:
# first page
curl "https://api.samo-trans.com/api/public/v1/proposals/search?type=CARGO&limit=50" \
-H "Authorization: Bearer $SAMO_KEY"
# next one
curl "https://api.samo-trans.com/api/public/v1/proposals/search?type=CARGO&limit=50&cursor=eyJvIjo1MH0" \
-H "Authorization: Bearer $SAMO_KEY"
The cursor is opaque: do not parse or construct it, just echo back exactly what the server gave you. Its format may change without a version bump.
Important: this cursor is not keyset
The cursor currently encodes an offset, not a position in a stable ordering. Two practical consequences follow:
- Deep pages get expensive. Walking thousands of pages to mirror the whole exchange is a poor pattern — slow for you and heavy for us.
- The feed shifts between pages. While you read page 3, new listings arrive; a record can reappear on another page or slip past you entirely.
So always deduplicate by id, and do not assume the union of all pages contains exactly
total distinct records. One more factor: reactivating a listing refreshes its createdAt,
which moves it up the ordering.
The right way to synchronise
To maintain your own copy of the data, pull a delta rather than walking everything — that is what
updatedSince is for:
curl "https://api.samo-trans.com/api/public/v1/proposals/search?updatedSince=2026-08-05T09:00:00.000Z&limit=100" \
-H "Authorization: Bearer $SAMO_KEY"
Only listings changed at or after that moment come back. The loop:
- Record the time your sync starts.
- Request pages with
updatedSinceset to the last successful sync time. - Apply changes locally, matching on
id(insert or update). - Store the new timestamp and repeat on a schedule.
Overlap slightly (say, a minute earlier than the last run). That is safe precisely because you
deduplicate by id, and it protects you from edge effects on the second boundary.
If you need to react in real time rather than on a schedule, see
webhooks: the proposal.matched event arrives on its own the moment a
matching listing is published.