Goal
By the end of this guide you will be able to search for contacts by email (useful for dedup checks), filter by custom field values, and paginate through large result sets.Prerequisites
- An API key with
contacts:readscope - Your API base URL (found on the Settings > API Keys page)
- A contact structure ID — search operates within a single contact structure. Retrieve yours with
GET /api/contact-structure(see Manage Custom Fields)
POST but is semantically a read operation. It requires only contacts:read scope (not contacts:write).
How search requests work
Every search request has two main parts: a filter that decides which contacts match, and a source array that decides which fields to return for each matching contact.The source array
source is a required projection that tells the API which fields to return in each contact. Pass an array containing one or more of these case-sensitive values:
_id: the contact’s unique identifierkey: the contact’s email addressfields: custom field valuestags: tag assignmentslists: list membershipscontactStatus: primary status (Active,Inactive, etc.)contactSubStatus: secondary statuscreatedAt: creation timestampupdatedAt: last-update timestamp
sourceis required and must contain at least one value.- Values are case-sensitive.
_idworks;_IDreturns a validation error. _idis always returned, even when you don’t include it insource.- Any unknown value rejects the entire request. There’s no partial success.
Steps
1. Search by email address
This is the most common search pattern, useful for deduplication before creating a contact.200 OK):
"operator": "EQ". If totalRecords is 0, the email is not in use and you can safely create the contact.
2. Search by email domain
Find all contacts from a specific email domain.3. Search by custom field values
Filter contacts based on custom field values. Use the field’s_id from your contact structure.
200 OK):
4. Combine multiple criteria
Use multiple criteria objects to build complex filters. Criteria within the same group are AND-ed; separate groups are OR-ed.5. Search within a specific list
Filter contacts that belong to a specific list.6. Paginate through results
For large result sets, increment thepage parameter to retrieve subsequent pages. Pages are 1-indexed.
200 OK):
totalRecords to calculate the total number of pages: Math.ceil(totalRecords / pageSize). Then loop through pages 1 to N.
Request body reference
| Field | Required | Description |
|---|---|---|
contactStructureId | Yes | The contact structure to search within |
page | Yes | Page number (1-indexed) |
pageSize | Yes | Results per page |
source | Yes | Field projection. Pass an array of one or more case-sensitive values from: _id, key, fields, tags, lists, contactStatus, contactSubStatus, createdAt, updatedAt. |
contactSpecification | Yes | Object with a filters array of filter groups (see operators below) |
sortField | No | Array of sort criteria |
showFieldIds | No | Limit which field IDs appear in fields array |
Available filter operators
| Operator | Description | Example use |
|---|---|---|
EQ | Equals | Exact email match |
NEQ | Not equals | Exclude a specific value |
SW | Starts with | Email prefix search |
CONTAINS | Contains substring | Partial name match |
GT | Greater than | Created after a date |
LT | Less than | Created before a date |
BETWEEN | Between two values | Date range |
NOT_BETWEEN | Not between two values | Exclude a date range |
IS_EMPTY | Field is empty | Find contacts with missing data |
NOT_EMPTY | Field is not empty | Find contacts with data present |
IN | In a set of values | Match any of several list IDs |
NIN | Not in a set | Exclude specific values |
Available filter columns
| Column | Description |
|---|---|
KEY | Email address |
EMAIL_DOMAIN | Email domain (e.g., example.com) |
FIELD_ID | Custom field value (requires id parameter with the field’s _id) |
TAG_ID | Tag assignment |
LIST_ID | List membership |
CONTACT_STATUS | Primary status (active, inactive, etc.) |
CONTACT_SUB_STATUS | Secondary status |
CREATED_AT | Creation timestamp |
UPDATED_AT | Last update timestamp |
Common errors
| Status | Error | Cause | Fix |
|---|---|---|---|
401 | UnauthorizedError | Invalid or inactive API key | Verify your key in Settings > API Keys |
403 | ForbiddenError | Key lacks contacts:read scope | Search requires contacts:read scope |
400 | RequiredFieldError | A required body field is missing | Ensure contactStructureId, page, pageSize, source, and contactSpecification are all in the request body |
400 | ModelValidationError | A field’s value fails schema validation | Check the error’s field and message. For source, confirm it’s a non-empty array of valid, case-sensitive values |
400 | ValidationError | Other validation failure (e.g., bad filter operator) | Read the message for specifics |
429 | TooManyRequestsError | Rate limit exceeded | Wait for the Retry-After period. See Rate Limits |
Next steps
- Manage Contacts — create, update, or delete the contacts you find
- Manage Lists — organize contacts into lists
- Manage Custom Fields — understand your contact structure’s field IDs
- Migration from Legacy — use search for dedup during bulk import