List contacts
const url = 'https://api.getsitecontrol.com/v1/sites/1/contacts?limit=50&order=oldest';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.getsitecontrol.com/v1/sites/1/contacts?limit=50&order=oldest' \ --header 'Authorization: Bearer <token>'Contacts of the site in ascending order of id (order=newest for the latest
first), each with its e-mail, status,
names, custom fields, tags, source and email_stats. Call it to find
contacts by filters, to export them or to keep a copy in sync. One contact by
id is get_contact; counts without the contacts are get_contacts_stats.
Filters combine: every one given must match. Deleted contacts are not listed.
A page holds up to limit contacts; while next_cursor is set, pass it as
cursor for the next page.
created_at is the date the contact was added at its source: imports and
Shopify keep the original date, so created_at does not grow with the id.
To keep a copy in sync, walk the whole list with the cursor and no filters
once. After that, pass updated_since set to the start of the previous walk
minus a margin of a few minutes, and merge by id. The margin is required:
a change may be stamped slightly earlier than it becomes visible. Deletions
are not visible: a deleted contact simply disappears, so repeat the full walk
now and then to notice them.
updated_since covers changes of the contact itself: its fields, status,
tags or e-mail, and a restore. It misses the rest. A deletion is not listed,
the contact disappears. Sending and opening e-mails, the e-mail quality check
and segment tracking do not move updated_at, so email_stats changes
without it. A contact not changed since changes began to be recorded,
created in the admin, or the account owner’s contact added with the site has
updated_at equal to created_at and is not found by updated_since, even
when its created_at is newer; the full walk lists such contacts.
segment_id applies the conditions of the segment as they are now.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Query Parameters
Section titled “Query Parameters”next_cursor of the previous page, sent with the same order; omit for the first
next_cursor of the previous page, sent with the same order; omit for the first
Items per page
Items per page
Comma-separated fields to keep in each item, for example id,name; each item then carries only those fields and id. Omit for every field
Comma-separated fields to keep in each item, for example id,name; each item then carries only those fields and id. Omit for every field
Order of the items: oldest first (default) or newest first; asc and desc mean the same
Order of the items: oldest first (default) or newest first; asc and desc mean the same
Any of these statuses; repeat for several. subscribed: subscribed, with double opt-in confirmed or not needed; unsubscribed: unsubscribed, never subscribed or awaiting double opt-in; bounced: the address bounced or failed the e-mail check
Any of these statuses; repeat for several. subscribed: subscribed, with double opt-in confirmed or not needed; unsubscribed: unsubscribed, never subscribed or awaiting double opt-in; bounced: the address bounced or failed the e-mail check
Tagged with any of these tag ids from list_tags; repeat for several
Tagged with any of these tag ids from list_tags; repeat for several
In any of these countries, ISO 3166-1 alpha-2 codes such as US; repeat for several
In any of these countries, ISO 3166-1 alpha-2 codes such as US; repeat for several
With any of these whole addresses, without case; repeat for several
With any of these whole addresses, without case; repeat for several
Part of the e-mail, phone, first or last name, without case; % and _ are plain characters. Digits also match the id. To find a contact by its address, use email
Part of the e-mail, phone, first or last name, without case; % and _ are plain characters. Digits also match the id. To find a contact by its address, use email
created_at at or after this moment; without an offset, taken as UTC; a plain date means its start
created_at at or after this moment; without an offset, taken as UTC; a plain date means its start
created_at before this moment; without an offset, taken as UTC; a plain date means its start
created_at before this moment; without an offset, taken as UTC; a plain date means its start
updated_at at or after this moment; without an offset, taken as UTC; a plain date means its start. Only contacts with a recorded change match
updated_at at or after this moment; without an offset, taken as UTC; a plain date means its start. Only contacts with a recorded change match
Matching the conditions of this segment, a segment id from list_segments
Matching the conditions of this segment, a segment id from list_segments
Matching a tree of conditions, as JSON: the language of create_segment conditions, without saving a segment; for example contacts without a first name: {“op”: “and”, “args”: [{“op”: “undefined”, “args”: [”{{$contact.firstName}}”]}]}
Matching a tree of conditions, as JSON: the language of create_segment conditions, without saving a segment; for example contacts without a first name: {“op”: “and”, “args”: [{“op”: “undefined”, “args”: [”{{$contact.firstName}}”]}]}
Responses
Section titled “Responses”One page of a list; next_cursor is null on the last page.
object
object
Subscribed: subscribed to e-mails. unsubscribed: opted out, never subscribed, or has not confirmed the subscription yet. bounced: e-mails to the address bounced, or it was cleaned from the list.
ISO 3166-1 alpha-2
IANA time zone name
ISO 639-1 code
Set values of custom fields by field id, typed by the field: a date as YYYY-MM-DD, a datetime in ISO 8601
object
object
Where the contact came from; widget, automation, import, shopify, mailchimp, klaviyo name the source. api: added through the public API or an AI assistant (MCP). manual: added by hand in the admin. other: a source not listed here.
Depends on the type: the widget, the automation, the admin user, the import task, the Shopify customer, the Mailchimp id; for api the API key, or the user whose access added it when there is no key
E-mails to the contact, the keys of EmailMetrics; send includes
e-mails rejected on sending, also counted in bounce.
object
When the contact was added at its source; imports keep the original date
Last change; equals created_at when none was recorded
Empty unless the contact could not be read in full; wording is not stable, do not parse
{ "items": [ { "status": "subscribed", "source": { "type": "widget" } } ]}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds
The documented shape of every error body on the versioned surface.
object
object
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "reason": "example", "next": "example", "request_id": "example", "errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ], "metadata": {}}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds
The documented shape of every error body on the versioned surface.
object
object
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "reason": "example", "next": "example", "request_id": "example", "errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ], "metadata": {}}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds
Authentication scheme: Bearer
The documented shape of every error body on the versioned surface.
object
object
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "reason": "example", "next": "example", "request_id": "example", "errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ], "metadata": {}}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds
segment_not_found: no segment segment_id on the site
The documented shape of every error body on the versioned surface.
object
object
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "reason": "example", "next": "example", "request_id": "example", "errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ], "metadata": {}}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds
segment_conditions_invalid: the conditions cannot be applied, the ones in conditions or the saved ones of the segment, which are fixed in the admin. validation_failed: a parameter is invalid. invalid_cursor: cursor was not issued here or was issued for another order
The documented shape of every error body on the versioned surface.
object
object
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "reason": "example", "next": "example", "request_id": "example", "errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ], "metadata": {}}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds
rate_limited: a request limit is spent; a request with search or segment_id also spends the heavy request limit
The documented shape of every error body on the versioned surface.
object
object
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "reason": "example", "next": "example", "request_id": "example", "errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ], "metadata": {}}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds
Seconds until the bucket refills
temporarily_unavailable: the request took too long, or limits or idempotency keys cannot be enforced; narrow the filters or retry later
The documented shape of every error body on the versioned surface.
object
object
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "reason": "example", "next": "example", "request_id": "example", "errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ], "metadata": {}}Headers
Section titled “Headers”API version the response was produced for
Request id, echoed from the request when valid
Limit of the tightest bucket
Requests left in its window
End of its window, epoch seconds