Skip to main content

Search Contacts

Find contacts by email address, custom field values, or other criteria using the search API.

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:read scope
  • 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)
Scope note: Contact search uses POST but is semantically a read operation. It requires only contacts:read scope (not contacts:write).

Steps

1. Search by email address

This is the most common search pattern, useful for deduplication before creating a contact.
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 10,
    "source": ["_id", "key", "fields", "createdAt", "updatedAt"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "KEY",
              "operator": "EQ",
              "values": ["jane.smith@example.com"]
            }
          ]
        }
      ]
    }
  }'
Response (200 OK):
{
  "contacts": [
    {
      "_id": "66f1a6e4698f1bca60424901",
      "key": "jane.smith@example.com",
      "fields": [
        { "_id": "64a1b2c3d4e5f6a7b8c9d100", "value": "Jane" },
        { "_id": "64a1b2c3d4e5f6a7b8c9d101", "value": "Smith" }
      ],
      "createdAt": "2026-03-28T14:30:00.000Z",
      "updatedAt": "2026-03-28T14:30:00.000Z",
      "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1"
    }
  ],
  "totalRecords": 1
}
Dedup pattern: Before creating a contact, search by email using "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.
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "EMAIL_DOMAIN",
              "operator": "EQ",
              "values": ["example.com"]
            }
          ]
        }
      ]
    }
  }'

3. Search by custom field values

Filter contacts based on custom field values. Use the field’s _id from your contact structure.
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields", "tags"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "FIELD_ID",
              "id": "64a1b2c3d4e5f6a7b8c9d102",
              "operator": "EQ",
              "values": ["Enterprise"]
            }
          ]
        }
      ]
    }
  }'
Response (200 OK):
{
  "contacts": [
    {
      "_id": "66f1a6e4698f1bca60424905",
      "key": "alice@enterprise-corp.com",
      "fields": [
        { "_id": "64a1b2c3d4e5f6a7b8c9d100", "value": "Alice" },
        { "_id": "64a1b2c3d4e5f6a7b8c9d101", "value": "Chen" },
        { "_id": "64a1b2c3d4e5f6a7b8c9d102", "value": "Enterprise" }
      ],
      "tags": [
        { "_id": "64a1b2c3d4e5f6a7b8c9d300" }
      ],
      "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1"
    }
  ],
  "totalRecords": 1
}

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.
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields", "createdAt"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "FIELD_ID",
              "id": "64a1b2c3d4e5f6a7b8c9d102",
              "operator": "EQ",
              "values": ["Enterprise"]
            },
            {
              "columnToFilter": "CREATED_AT",
              "operator": "GT",
              "values": ["2026-01-01T00:00:00.000Z"]
            }
          ]
        }
      ]
    }
  }'
This searches for contacts where the custom field equals “Enterprise” AND the contact was created after January 1, 2026.

5. Search within a specific list

Filter contacts that belong to a specific list.
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "LIST_ID",
              "operator": "IN",
              "values": ["66f1a6e4698f1bca60425001"]
            }
          ]
        }
      ]
    }
  }'

6. Paginate through results

For large result sets, increment the page parameter to retrieve subsequent pages. Pages are 1-indexed.
# Page 1 (first 50 results)
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 50,
    "source": ["_id", "key"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "CONTACT_STATUS",
              "operator": "EQ",
              "values": ["active"]
            }
          ]
        }
      ]
    },
    "sortField": [
      {
        "column": "CREATED_AT",
        "order": "desc"
      }
    ]
  }'
Response (200 OK):
{
  "contacts": [
    { "_id": "66f1a6e4698f1bca60424950", "key": "newest@example.com" },
    { "_id": "66f1a6e4698f1bca60424949", "key": "second@example.com" }
  ],
  "totalRecords": 3847
}
Use totalRecords to calculate the total number of pages: Math.ceil(totalRecords / pageSize). Then loop through pages 1 to N.

Request body reference

FieldRequiredDescription
contactStructureIdYesThe contact structure to search within
pageYesPage number (1-indexed)
pageSizeYesResults per page
sourceYesFields to include in results: _id, key, fields, tags, lists, contactStatus, contactSubStatus, createdAt, updatedAt
contactSpecificationYesObject with a filters array of filter groups (see operators below)
sortFieldNoArray of sort criteria
showFieldIdsNoLimit which field IDs appear in fields array

Available filter operators

OperatorDescriptionExample use
EQEqualsExact email match
NEQNot equalsExclude a specific value
SWStarts withEmail prefix search
CONTAINSContains substringPartial name match
GTGreater thanCreated after a date
LTLess thanCreated before a date
BETWEENBetween two valuesDate range
NOT_BETWEENNot between two valuesExclude a date range
IS_EMPTYField is emptyFind contacts with missing data
NOT_EMPTYField is not emptyFind contacts with data present
INIn a set of valuesMatch any of several list IDs
NINNot in a setExclude specific values

Available filter columns

ColumnDescription
KEYEmail address
EMAIL_DOMAINEmail domain (e.g., example.com)
FIELD_IDCustom field value (requires id parameter with the field’s _id)
TAG_IDTag assignment
LIST_IDList membership
CONTACT_STATUSPrimary status (active, inactive, etc.)
CONTACT_SUB_STATUSSecondary status
CREATED_ATCreation timestamp
UPDATED_ATLast update timestamp

Common Errors

StatusErrorCauseFix
401UnauthorizedErrorInvalid or inactive API keyVerify your key in Settings > API Keys
403ForbiddenErrorKey lacks contacts:read scopeSearch requires contacts:read scope
400ValidationErrorMissing required fields or invalid filter operatorEnsure contactStructureId, page, pageSize, source, and contactSpecification are all present
429TooManyRequestsErrorRate limit exceededWait for the Retry-After period. See Rate Limits

Next Steps