GET /contacts is the export. There is no separate endpoint, and no job to
start: you read pages until the cursor is null.
Read every contact
Setlimit to 500, then follow next_cursor:
next_cursor as cursor on the next request. Stop when has_more is
false.
Read only what changed
Sendupdated_since with the timestamp of your last successful run:
updated_at of the last contact that you read, and use it as the
updated_since of your next run. Take a small overlap, for example one
minute, so a contact that changed during the run cannot fall between two runs.
Why the order is what it is
Kai returns contacts oldest change first, byupdated_at and then by id.
An export of 40,000 contacts takes many requests, and your workspace keeps
editing contacts while it runs. An edited contact moves to the end of this
order, never backward, so the export can see it twice but cannot miss it.
A newest-first order has the opposite property, and an export would silently
drop rows.
Filters
The filters work together. This request lists the contacts of one teammate
that changed today: